1 c string types declaration char
几个星期前,我开始学习编程语言C.我对网络技术有所了解,如HMTL/CSS,Javscript,PHP和基本服务器管理,但C让我感到困惑.据我所知,C语言没有字符串的数据类型,只有字符,但是我可能错了.
我听说有两种声明字符串的方法.声明字符串的这两行之间有什么区别:
a.)char stringName[];
b.)char *stringName;
我得到的char stringName[];是一个字符数组.但是,第二行让我感到困惑.据我所知,第二行是一个指针变量.是不是指针变量应该是另一个变量的内存地址?
在C语言中,正如您所说,"字符串"是一个数组char.C规范中内置的大多数字符串函数都希望字符串为"NUL终止",这意味着char字符串的最后一个是a 0.不是代表数字零的代码,而是代表的实际值0.
例如,如果您的平台使用ASCII,则以下"字符串"为"ABC":
char myString[4] = {65, 66, 67, 0};
Run Code Online (Sandbox Code Playgroud)
当您使用char varName[] = "foo"语法时,您将在堆栈上分配字符串(或者如果它在全局空间中,您将全局分配,但不是动态分配.)
C语言中的内存管理比您可能遇到的许多其他语言更加手动.特别是,存在"指针"的概念.
char *myString = "ABC"; /* Points to a string somewhere in memory, the compiler puts somewhere. */
Run Code Online (Sandbox Code Playgroud)
现在,a char *是"指向char或char数组的地址".请注意该声明中的"或",程序员必须知道具体情况.
确保您执行的任何字符串操作都不超过您为指针分配的内存量也很重要.
char myString[5];
strcpy(myString, "12345"); /* copy "12345" into myString.
* On no! I've forgot space for my nul terminator and
* have overwritten some memory I don't own. */
Run Code Online (Sandbox Code Playgroud)
"12345"实际上是6个字符长(不要忘记0在最后),但我只保留了5个字符.这就是所谓的"缓冲区溢出",并且是许多严重错误的原因.
"[]"和"*"之间的另一个区别是,正在创建一个数组(正如您猜测的那样).另一个不是保留任何空间(除了保持指针本身的空间.)这意味着直到你指出它知道有效的地方,不应该使用指针的值来读或写.
另一点(由评论中的某人制作)
您不能将数组作为参数传递给C中的函数.尝试时,它会自动转换为指针.这就是为什么我们将指针传递给字符串而不是字符串本身
在 C 中,字符串是后跟 0 值字节1的字符值序列。所有处理字符串的库函数都使用 0 终止符来标识字符串的结尾。字符串存储为 的数组char,但并非所有 的数组都char包含字符串。
例如,字符串"hello"表示为字符序列{'h', 'e', 'l', 'l', 'o', 0}2char要存储该字符串,您需要一个包含- 5 个字符加上 0 终止符 的 6 元素数组:
char greeting[6] = "hello";
Run Code Online (Sandbox Code Playgroud)
或者
char greeting[] = "hello";
Run Code Online (Sandbox Code Playgroud)
在第二种情况下,数组的大小是根据用于初始化它的字符串的大小计算的(计算 0 终止符)。在这两种情况下,您都会创建一个 6 元素数组char并将字符串文字的内容复制到其中。除非数组是在文件范围(任何函数之外)或使用static关键字声明的,否则它仅在声明的块的持续时间内存在。
字符串文字 "hello"也存储在 的 6 元素数组中char,但其存储方式是在程序加载到内存时分配并保留到程序终止3,并且在整个程序中都是可见的。当你写的时候
char *greeting = "hello";
Run Code Online (Sandbox Code Playgroud)
您正在将包含字符串文字的数组的第一个元素的地址greeting分配给指针变量。
一如既往,一张图片胜过一千个文字。这是一个简单的小程序:
#include <string.h>
#include <stdio.h>
#include <ctype.h>
int main( void )
{
char greeting[] = "hello"; // greeting contains a *copy* of the string "hello";
// size is taken from the length of the string plus the
// 0 terminator
char *greetingPtr = "hello"; // greetingPtr contains the *address* of the
// string literal "hello"
printf( "size of greeting array: %zu\n", sizeof greeting );
printf( "length of greeting string: %zu\n", strlen( greeting ) );
printf( "size of greetingPtr variable: %zu\n", sizeof greetingPtr );
printf( "address of string literal \"hello\": %p\n", (void * ) "hello" );
printf( "address of greeting array: %p\n", (void * ) greeting );
printf( "address of greetingPtr: %p\n", (void * ) &greetingPtr );
printf( "content of greetingPtr: %p\n", (void * ) greetingPtr );
printf( "greeting: %s\n", greeting );
printf( "greetingPtr: %s\n", greetingPtr );
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是输出:
size of greeting array: 6
length of greeting string: 5
size of greetingPtr variable: 8
address of string literal "hello": 0x4007f8
address of greeting array: 0x7fff59079cf0
address of greetingPtr: 0x7fff59079ce8
content of greetingPtr: 0x4007f8
greeting: hello
greetingPtr: hello
Run Code Online (Sandbox Code Playgroud)
sizeof请注意和之间的区别strlen-strlen计算直到(但不包括)0 终止符的所有字符。
所以这就是内存中的情况:
Item Address 0x00 0x01 0x02 0x03
---- ------- ---- ---- ---- ----
"hello" 0x4007f8 'h' 'e' 'l' 'l'
0x4007fc 'o' 0x00 ??? ???
...
greetingPtr 0x7fff59079ce8 0x00 0x00 0x00 0x00
0x7fff59879cec 0x00 0x40 0x7f 0xf8
greeting 0x7fff59079cf0 'h' 'e' 'l' 'l'
0x7fff59079cf4 'o' 0x00 ??? ???
Run Code Online (Sandbox Code Playgroud)
字符串文字"hello"存储在不同的低地址(在我的系统上,这对应于.rodata可执行文件的部分,用于静态、常量数据)。变量greeting和greetingPtr存储在更高的地址,对应于我系统上的堆栈。如您所见,greetingPtr存储字符串文字的地址"hello",同时greeting存储字符串内容的副本。
这就是事情可能变得有点混乱的地方。我们看一下下面的打印语句:
printf( "greeting: %s\n", greeting );
printf( "greetingPtr: %s\n", greetingPtr );
Run Code Online (Sandbox Code Playgroud)
greeting是 的 6 元素数组char,并且greetingPtr是指向 的指针,但我们以完全相同的方式char将它们传递给,并且字符串被正确打印出来;printf那怎么行得通呢?
除非它是 thesizeof或一元运算&符的操作数,或者是用于在声明中初始化另一个数组的字符串文字,否则类型“N-element array of ”的表达式T将被转换(“衰减”)为类型“的表达式”指向T" 的指针,表达式的值将是数组第一个元素的地址。
在printf调用中,表达式的greeting类型为“6-element array of char”;由于它不是 thesizeof或一元&运算符的操作数,因此它被转换(“衰减”)为“指向char”的指针 ( char *) 类型的表达式,并且第一个元素的地址实际上传递给printf。IOW,它的行为与下一个调用4中的表达式完全相同。greetingPtrprintf
转换%s说明符告诉printf它相应的参数具有 type char *,并且它应该打印出从该地址开始的字符值,直到看到 0 终止符。
希望有点帮助。
NUL终结者;不应将其与NULL指针常量混淆,指针常量也是 0 值,但在不同的上下文中使用。'\0'。前导反斜杠会“转义”该值,因此不会将其视为字符 '0'(ASCII 48),而是将其视为值 0(ASCII 0))。greeting 复制到数组,而 的声明则greetingPtr复制字符串第一个元素的地址。字符串文字"hello"也是一个数组表达式。在第一个声明中,由于它用于初始化声明中的另一个数组,因此会复制该数组的内容。在第二个声明中,目标是指针,而不是数组,因此表达式从数组类型转换为指针类型,并将得到的指针值复制到变量。
| 归档时间: |
|
| 查看次数: |
3746 次 |
| 最近记录: |