我现在正在学习C并且对字符数组 - 字符串感到有些困惑。
char name[15]="Fortran";
没问题 - 它的数组可以容纳(最多?)15个字符
char name[]="Fortran";
C计算我的字符数,所以我没有 - 整洁!
char* name;
好。现在怎么办?我所知道的是,这可以容纳大量后来分配的字符(例如:通过用户输入),但是
答案 0 :(得分:32)
答案 1 :(得分:3)
这是一个指针。这意味着它是一个在内存中保存地址的变量。它“指向”另一个变量。
实际上它本身不能容纳大量的字符。它本身只能在内存中保存一个地址。如果在创建时为其分配字符,它将为这些字符分配空间,然后指向该地址。你可以这样做:
char* name = "Mr. Anderson";
实际上与此基本相同:
char name[] = "Mr. Anderson";
字符指针派上用场的地方是动态内存。您可以通过执行以下操作,在程序中随时为char指针指定任意长度的字符串:
char *name;
name = malloc(256*sizeof(char));
strcpy(name, "This is less than 256 characters, so this is fine.");
或者,您可以使用strdup()
功能为其分配,如下所示:
char *name;
name = strdup("This can be as long or short as I want. The function will allocate enough space for the string and assign return a pointer to it. Which then gets assigned to name");
如果以这种方式使用字符指针 - 并为其分配内存,则必须在重新分配之前释放名称中包含的内存。像这样:
if(name)
free(name);
name = 0;
在尝试释放内存之前,请确保检查该名称实际上是一个有效点。这就是if语句的作用。
你看到字符指针在C中被大量使用的原因是因为它们允许你用不同大小的字符串重新分配字符串。静态字符数组不会这样做。他们也更容易传球。
此外,字符指针很方便,因为它们可用于指向不同的静态分配字符数组。像这样:
char *name;
char joe[] = "joe";
char bob[] = "bob";
name = joe;
printf("%s", name);
name = bob;
printf("%s", name);
当您将静态分配的数组传递给带有字符指针的函数时,通常会发生这种情况。例如:
void strcpy(char *str1, char *str2);
如果你通过了那个:
char buffer[256];
strcpy(buffer, "This is a string, less than 256 characters.");
它将通过str1和str2来操纵它们,它们只是指向缓冲区和字符串文字存储在内存中的指针。
在函数中工作时要记住的事情。如果您有一个返回字符指针的函数,请不要返回指向函数中分配的静态字符数组的指针。它将超出范围,你会遇到问题。重复一遍,不要这样做:
char *myFunc() {
char myBuf[64];
strcpy(myBuf, "hi");
return myBuf;
}
那不行。在这种情况下,您必须使用指针并分配内存(如前所示)。分配的内存将保持不变,即使您传出函数范围也是如此。只是不要忘记如前所述释放它。
这比我想要的更加百科全书,希望它有用。
编辑删除C ++代码。我经常把两者混在一起,我有时会忘记。
答案 2 :(得分:2)
char * name只是一个指针。沿着行内存的某处必须分配内存,并将该内存的地址存储在 name 中。
答案 3 :(得分:2)
在C中,字符串实际上只是一个字符数组,正如您可以从定义中看到的那样。然而,从表面上看,任何数组都只是指向其第一个元素的指针,请参阅下面的细微复杂性。 C中没有范围检查,您在变量声明中提供的范围仅对变量的内存分配有意义。
a[x]
与*(a + x)
相同,即取消引用指针a增加x。
如果您使用以下内容:
char foo[] = "foobar";
char bar = *foo;
栏将设为'f'
为了避免混淆并避免误导人们,在指针和数组之间更复杂的区别上加上一些额外的话,谢谢avakar:
在某些情况下,指针实际上在语义上与数组不同,是一个(非详尽的)示例列表:
//sizeof
sizeof(char*) != sizeof(char[10])
//lvalues
char foo[] = "foobar";
char bar[] = "baz";
char* p;
foo = bar; // compile error, array is not an lvalue
p = bar; //just fine p now points to the array contents of bar
// multidimensional arrays
int baz[2][2];
int* q = baz; //compile error, multidimensional arrays can not decay into pointer
int* r = baz[0]; //just fine, r now points to the first element of the first "row" of baz
int x = baz[1][1];
int y = r[1][1]; //compile error, don't know dimensions of array, so subscripting is not possible
int z = r[1]: //just fine, z now holds the second element of the first "row" of baz
最后是一段有趣的琐事;由于a[x]
相当于*(a + x)
,因此您可以实际使用,例如'3 [a]'访问数组a的第四个元素。即以下是完全合法的代码,并将'b'打印为字符串foo的第四个字符。
#include <stdio.h>
int main(int argc, char** argv) {
char foo[] = "foobar";
printf("%c\n", 3[foo]);
return 0;
}
答案 4 :(得分:2)
char *name
,就其本身而言,无法容纳任何字符。这很重要。
char *name
只是声明name
是一个指针(即一个值为地址的变量),它将用于存储一个或多个字符的地址。程序。但是,它不会在内存中分配任何空间来实际保存这些字符,也不保证name
甚至包含有效地址。同样,如果您有int number
之类的声明,则在明确设置之前无法知道number
的值是什么。
就像声明一个整数的值一样,您可能稍后设置其值(number = 42
),在声明指向char的指针后,您可能稍后将其值设置为包含字符的有效内存地址 - 或您感兴趣的字符序列。
答案 5 :(得分:2)
确实令人困惑。理解和区分的重要一点是char name[]
声明数组,char* name
声明指针。这两个是不同的动物。
但是,C中的数组可以隐式转换为指向其第一个元素的指针。这使您能够执行指针运算并迭代数组元素(无论是什么类型的元素,char
都没关系)。正如@which所提到的,您可以使用索引运算符或指针算法来访问数组元素。实际上,索引运算符只是指针运算的一种语法糖(同一表达式的另一种表示)。
将数组和指针之间的差异区分为数组的第一个元素非常重要。可以使用char name[15]
运算符查询声明为sizeof
的数组的大小:
char name[15] = { 0 };
size_t s = sizeof(name);
assert(s == 15);
但如果您将sizeof
应用于char* name
,您将获得平台上指针的大小(即4个字节):
char* name = 0;
size_t s = sizeof(name);
assert(s == 4); // assuming pointer is 4-bytes long on your compiler/machine
此外,char元素数组的两种形式的定义是等价的:
char letters1[5] = { 'a', 'b', 'c', 'd', '\0' };
char letters2[5] = "abcd"; /* 5th element implicitly gets value of 0 */
数组的双重性质,数组隐式转换为指向其第一个元素的指针,在C(以及C ++)语言中,指针可用作遍历数组元素的迭代器:
/ *skip to 'd' letter */
char* it = letters1;
for (int i = 0; i < 3; i++)
it++;
答案 6 :(得分:1)
一个是实际的数组对象,另一个是引用或指针到这样的数组对象。
令人困惑的是,两者都有第一个字符的地址,但只是因为一个地址是第一个字符而另一个地址是内存中包含角色的地址。
可以在&name
的值中看到差异。在前两种情况下,它与name
的值相同,但在第三种情况下,它是一个不同的类型,称为指向char 或**char
的指针,它是指针本身的地址。也就是说,它是一个双间接指针。
#include <stdio.h>
char name1[] = "fortran";
char *name2 = "fortran";
int main(void) {
printf("%lx\n%lx %s\n", (long)name1, (long)&name1, name1);
printf("%lx\n%lx %s\n", (long)name2, (long)&name2, name2);
return 0;
}
Ross-Harveys-MacBook-Pro:so ross$ ./a.out
100001068
100001068 fortran
100000f58
100001070 fortran