char * 的范围与 C 中的 char 数组相比
Scope of char * compared with char array in C
同一变量的这两种形式,当在功能块的范围内定义时,我认为应该具有相同的范围,即在定义它们的功能块{...}
中:
char str1[] = "int_1 < int_2";
char *str1 = "int_1 < int_2";
但我的观察是 char *
超出了功能范围,而 char []
不复存在。在这两种情况下,符号名称 str1
都指向创建变量的内存位置,那么为什么一个 似乎 存在于函数之外,而另一个却没有?以下代码可用于测试此行为:(将 #define
从 0
更改为 1
选择一种形式而不是另一种形式以供说明。)
另请注意,虽然 static
修饰符可用于修改作用域,但这里有意不使用它来观察没有它的行为。
#define DO (1) //define as either 1 or 0
char * compare_int(int x1, int x2);
int main(void)
{
int a = 0;
int b = 0;
int c = '\n';
srand(clock()/CLOCKS_PER_SEC);
while(c != 'q')
{
a = rand()%3;
b = rand()%3;
printf("%s\n( enter 'q' to exit. )\n\n", compare_int(a, b));
c = getchar();
}
return 0;
}
char * compare_int(int x, int y)
{
printf("%d %d\n", x, y);
#if(DO)
char str1[] = "int_1 < int_2";
char str2[] = "int_1 == int_2";
char str3[] = "int_1 > int_2";
#else
char *str1 = "int_1 < int_2";
char *str2 = "int_1 == int_2";
char *str3 = "int_1 > int_2";
#endif
return x < y ? (str1) : x == y ? (str2) : (str3);
}
我已阅读 this,它确实回答了这个问题的一些关键部分,但对我代码中的任何 UB 进行了评论,and/or 引用了 C99 或指向段落的更新标准) 区分这两种形式,我们也将不胜感激。
在函数内具有自动存储持续时间的这些声明中
char str1[] = "int_1 < int_2";
char *str1 = "int_1 < int_2";
这两个标识符具有相同的函数作用域,并且在函数之外不存在。
也就是数组占用的内存和指针本身占用的内存在函数退出后就失效了。例如它可以被覆盖。
不同之处在于指针str1
指向一个具有静态存储持续时间的字符串文字。所以你可以 return 来自函数的指针,因为字符串文字将是活动的并且 returned 指针将指向它。
至于数组str1
则由字符串字面量初始化(通过将字符串字面量的元素复制到它自己的元素中),但它本身具有自动存储持续时间。因此,您不能将数组指示符用作 return 表达式,因为 returned 指针将无效,因为数组在退出函数后将不再存在。
这个:
char str1[] = "int_1 < int_2";
定义一个用给定字符串文字初始化的数组。如果你 return str1
,因为数组名称衰减到指向其第一个元素的指针,你正在 return 指向局部变量的指针。该变量的生命周期在函数 returns 时结束,并试图随后使用该地址调用 undefined behavior.
这在 C standard 的第 6.2.4p2 节中有记录:
The lifetime of an object is the portion of program
execution during which storage is guaranteed to be reserved
for it. An object exists, has a constant address, and retains
its last-stored value throughout its lifetime. If an object
is referred to outside of its lifetime, the behavior is
undefined. The value of a pointer becomes indeterminate when the
object it points to (or just past) reaches the end of its lifetime.
相比之下,这个:
char *str1 = "int_1 < int_2";
定义一个指针,它用字符串文字的地址初始化。字符串常量具有完整的程序生命周期,因此读取指向一个的指针是安全的。在这种情况下,当你 return str1
时,你 returning str1
的 value (不是它的地址)是字符串文字的地址。
字符串文字的生命周期在 C standard:
的第 6.4.5p6 节中指定
In translation phase 7, a byte or code of value zero is
appended to each multibyte character sequence that results from a
string literal or literals. The multibyte character sequence is
then used to initialize an array of static storage duration
and length just sufficient to contain the sequence.
和静态存储时长定义在6.2.4p3:
An object whose identifier is declared without
the storage-class specifier _Thread_local
, and either with
external or internal linkage or with the storage-class specifier static
,
has static storage duration. Its lifetime is the entire
execution of the program and its stored value is initialized only
once, prior to program startup.
使用 char[] 时,您的局部变量 - 分配在堆栈上 - 是一个字符数组。你从函数中 return 得到的是指向这个局部变量的指针。然而,一旦你从函数中 return 并且你的指针 return 一直指向堆栈上的某个位置,该位置迟早会被其他东西覆盖,局部变量就会消失。
使用 char * 你的局部变量只是一个指针,指向一个永远存在的常量。你return复制这个指针,完全没问题。
返回指向堆栈上局部变量的指针是一种会让您发疯的错误,因为首先 returned 字符串可能看起来没问题,但一段时间后它会随机出现将被覆盖。在创建字符串时,您将继续调试您怀疑会破坏字符串的代码,而没有意识到错误是在更早的时候设置的。
如果您 return
来自被调用函数的指针,您不会 return 对指针本身的 引用。
相反,指针的值 - 实际上是字符串文字的第一个元素的地址,这里 f.e。 "int_1 < int_2"
,分配给它 - returned by value,而不是指针本身 by reference.
程序终止之前,字符串文字本身驻留在只读内存中。
实际上,指向char
(char *
)的指针和char
(char[]
)的数组具有相同的存储空间class auto
并且仅对函数 compare_int
可见(具有函数局部作用域)。
函数执行一次后,它们都不再存在(在内存中),因此也不再可见。
printf()
调用中使用的值实际上是通过值传递的字符串字面量的第一个元素的地址。与被调用函数中的指针无关,这里strN
.
字符串文字未绑定到特定指针。
如果它们使用存储-class 说明符 static
进行限定,那么它们的对象将一直存在于内存中直到程序终止,通过不同的函数调用保留它们的值并且在您随处可见通过在调用者中传递指向它们的指针获得了对它们实际对象的引用。
但即便如此,returned 指针并不是对指针本身的引用,它的值——字符串文字的第一个元素的地址——是按值returned 的。
如果您将被调用函数中的指针视为 "holder" 或更好的 "delivery person",您可以想象得更好,就像友好地从 Amazon 送货的指针一样。 S/he 仅在一定时间内持有地址,但此后 s/he 将值提供给另一个人。
这与 return 从 compare_int
中获取地址值时类似。被调用函数 strN
中的指针将地址值提供给调用者。在那里它被当作 printf()
.
的参数
所以,我在 CodeBlocks IDE 中执行了你的代码,这是我发现的:-
案例 1
在compare_int(int x, int y)
中,当x = 2 & y = 0,在str1, str2, and str3 character arrays(char [])
声明之前,str1, 2 & 3
包含一些垃圾值,然后在某些内存地址的函数中局部声明它们,并为它们分配值。
然后根据条件,address of str3
成功返回给调用函数,也就是一个char *
到那个地址。但是当 printf("%s\n( enter 'q' to exit. )\n\n", compare_int(a, b));
printf()
试图通过 compare_int()
函数返回的 char *
读取该地址时,它会在那里找到一些垃圾值。这意味着 str1[], str2[], and str3[]
数组的值是该函数的局部值,并且不会在 complex_int()
函数之外持续存在。
案例 0
当str1, str2, and str3
声明并定义为constant strings
时,它们的值由函数返回并被printf()
成功读取,这意味着它们的值在不同的函数调用之间保持不变。
结论:
根据这种行为,我可以说在 case 0 中,string literals
存储在 permanent storage area
和本地 char *
中存储在stack
指向那个string literal
,当我们return
这个char *
,它returns它的值是那个string literal
的地址这就是它打印成功的原因。另一方面,在 case 1 中,char []
存储在 stack
中并且在 parent function
中是本地的,并且它们的值不会在两者之间持续存在不同的函数调用。这就是为什么无法从 main()
.
访问它们的原因
同一变量的这两种形式,当在功能块的范围内定义时,我认为应该具有相同的范围,即在定义它们的功能块{...}
中:
char str1[] = "int_1 < int_2";
char *str1 = "int_1 < int_2";
但我的观察是 char *
超出了功能范围,而 char []
不复存在。在这两种情况下,符号名称 str1
都指向创建变量的内存位置,那么为什么一个 似乎 存在于函数之外,而另一个却没有?以下代码可用于测试此行为:(将 #define
从 0
更改为 1
选择一种形式而不是另一种形式以供说明。)
另请注意,虽然 static
修饰符可用于修改作用域,但这里有意不使用它来观察没有它的行为。
#define DO (1) //define as either 1 or 0
char * compare_int(int x1, int x2);
int main(void)
{
int a = 0;
int b = 0;
int c = '\n';
srand(clock()/CLOCKS_PER_SEC);
while(c != 'q')
{
a = rand()%3;
b = rand()%3;
printf("%s\n( enter 'q' to exit. )\n\n", compare_int(a, b));
c = getchar();
}
return 0;
}
char * compare_int(int x, int y)
{
printf("%d %d\n", x, y);
#if(DO)
char str1[] = "int_1 < int_2";
char str2[] = "int_1 == int_2";
char str3[] = "int_1 > int_2";
#else
char *str1 = "int_1 < int_2";
char *str2 = "int_1 == int_2";
char *str3 = "int_1 > int_2";
#endif
return x < y ? (str1) : x == y ? (str2) : (str3);
}
我已阅读 this,它确实回答了这个问题的一些关键部分,但对我代码中的任何 UB 进行了评论,and/or 引用了 C99 或指向段落的更新标准) 区分这两种形式,我们也将不胜感激。
在函数内具有自动存储持续时间的这些声明中
char str1[] = "int_1 < int_2";
char *str1 = "int_1 < int_2";
这两个标识符具有相同的函数作用域,并且在函数之外不存在。
也就是数组占用的内存和指针本身占用的内存在函数退出后就失效了。例如它可以被覆盖。
不同之处在于指针str1
指向一个具有静态存储持续时间的字符串文字。所以你可以 return 来自函数的指针,因为字符串文字将是活动的并且 returned 指针将指向它。
至于数组str1
则由字符串字面量初始化(通过将字符串字面量的元素复制到它自己的元素中),但它本身具有自动存储持续时间。因此,您不能将数组指示符用作 return 表达式,因为 returned 指针将无效,因为数组在退出函数后将不再存在。
这个:
char str1[] = "int_1 < int_2";
定义一个用给定字符串文字初始化的数组。如果你 return str1
,因为数组名称衰减到指向其第一个元素的指针,你正在 return 指向局部变量的指针。该变量的生命周期在函数 returns 时结束,并试图随后使用该地址调用 undefined behavior.
这在 C standard 的第 6.2.4p2 节中有记录:
The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.
相比之下,这个:
char *str1 = "int_1 < int_2";
定义一个指针,它用字符串文字的地址初始化。字符串常量具有完整的程序生命周期,因此读取指向一个的指针是安全的。在这种情况下,当你 return str1
时,你 returning str1
的 value (不是它的地址)是字符串文字的地址。
字符串文字的生命周期在 C standard:
的第 6.4.5p6 节中指定In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals. The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence.
和静态存储时长定义在6.2.4p3:
An object whose identifier is declared without
the storage-class specifier_Thread_local
, and either with external or internal linkage or with the storage-class specifierstatic
, has static storage duration. Its lifetime is the entire execution of the program and its stored value is initialized only once, prior to program startup.
使用 char[] 时,您的局部变量 - 分配在堆栈上 - 是一个字符数组。你从函数中 return 得到的是指向这个局部变量的指针。然而,一旦你从函数中 return 并且你的指针 return 一直指向堆栈上的某个位置,该位置迟早会被其他东西覆盖,局部变量就会消失。
使用 char * 你的局部变量只是一个指针,指向一个永远存在的常量。你return复制这个指针,完全没问题。
返回指向堆栈上局部变量的指针是一种会让您发疯的错误,因为首先 returned 字符串可能看起来没问题,但一段时间后它会随机出现将被覆盖。在创建字符串时,您将继续调试您怀疑会破坏字符串的代码,而没有意识到错误是在更早的时候设置的。
如果您 return
来自被调用函数的指针,您不会 return 对指针本身的 引用。
相反,指针的值 - 实际上是字符串文字的第一个元素的地址,这里 f.e。 "int_1 < int_2"
,分配给它 - returned by value,而不是指针本身 by reference.
程序终止之前,字符串文字本身驻留在只读内存中。
实际上,指向char
(char *
)的指针和char
(char[]
)的数组具有相同的存储空间class auto
并且仅对函数 compare_int
可见(具有函数局部作用域)。
函数执行一次后,它们都不再存在(在内存中),因此也不再可见。
printf()
调用中使用的值实际上是通过值传递的字符串字面量的第一个元素的地址。与被调用函数中的指针无关,这里strN
.
字符串文字未绑定到特定指针。
如果它们使用存储-class 说明符 static
进行限定,那么它们的对象将一直存在于内存中直到程序终止,通过不同的函数调用保留它们的值并且在您随处可见通过在调用者中传递指向它们的指针获得了对它们实际对象的引用。
但即便如此,returned 指针并不是对指针本身的引用,它的值——字符串文字的第一个元素的地址——是按值returned 的。
如果您将被调用函数中的指针视为 "holder" 或更好的 "delivery person",您可以想象得更好,就像友好地从 Amazon 送货的指针一样。 S/he 仅在一定时间内持有地址,但此后 s/he 将值提供给另一个人。
这与 return 从 compare_int
中获取地址值时类似。被调用函数 strN
中的指针将地址值提供给调用者。在那里它被当作 printf()
.
所以,我在 CodeBlocks IDE 中执行了你的代码,这是我发现的:-
案例 1
在compare_int(int x, int y)
中,当x = 2 & y = 0,在str1, str2, and str3 character arrays(char [])
声明之前,str1, 2 & 3
包含一些垃圾值,然后在某些内存地址的函数中局部声明它们,并为它们分配值。
然后根据条件,address of str3
成功返回给调用函数,也就是一个char *
到那个地址。但是当 printf("%s\n( enter 'q' to exit. )\n\n", compare_int(a, b));
printf()
试图通过 compare_int()
函数返回的 char *
读取该地址时,它会在那里找到一些垃圾值。这意味着 str1[], str2[], and str3[]
数组的值是该函数的局部值,并且不会在 complex_int()
函数之外持续存在。
案例 0
当str1, str2, and str3
声明并定义为constant strings
时,它们的值由函数返回并被printf()
成功读取,这意味着它们的值在不同的函数调用之间保持不变。
结论:
根据这种行为,我可以说在 case 0 中,string literals
存储在 permanent storage area
和本地 char *
中存储在stack
指向那个string literal
,当我们return
这个char *
,它returns它的值是那个string literal
的地址这就是它打印成功的原因。另一方面,在 case 1 中,char []
存储在 stack
中并且在 parent function
中是本地的,并且它们的值不会在两者之间持续存在不同的函数调用。这就是为什么无法从 main()
.