C语言—字符串数组与指针的区别

作者:追风剑情 发布于:2019-10-15 21:17 分类:C

const char ar1[29] = "this is a char array.";
const char *pt1 = "this is a char array.";

       数组形式(ar1[])在计算机的内存中分配为一个内含22个元素的数组(每个元素对应一个字符,还加上一个末尾的空字符‘\0’),每个元素被初始化为字符串字面量对应的字符。通常,字符串都作为可执行文件的一部分储存在数据段中。当把程序载入内存时,也载入了程序中的字符串。字符串储存在静态存储区(static memory)中。但是,程序在开始运行时才会为该数组分配内存。此时,才将字符串拷贝到数组中。注意,此时字符串有两个副本。一个是在静态内存中的字符串字面量,另一个是储存在ar1数组中的字符串。

       此后,编译器便把数组名ar1识别为该数组首元素地址(&ar1[0])的别名。这里关键要理解,在数组形式中,ar1是地址常量。不能更改ar1,如果改变了ar1,则意味着改变了数组的存储位置(即地址)。可以进行类似ar1+1这样的操作,标识数组的下一个元素。但是不允许进行++ar1这样的操作。递增运算符只能用于变量名前(或概括地说,只能用于可修改的左值),不能用于常量。

       指针形式(*pt1)也使得编译器为字符串在静态存储区预留22个元素的空间。另外,一旦开始执行程序,它会为指针变量pt1留出一个储存位置,并把字符串的地址储存在指针变量中。该变量最初指向该字符串的首字符,但是它的值可以改变。因此,可以使用递增运算符。例如,++pt1将指向第2个字符(h)。

       字符串字面量被视为const数据。由于pt1指向这个const数据,所以应该把pt1声明为指向const数据的指针。这意味着不能用pt1改变它所指向的数据,但是仍然可以改变pt1的值(即,pt1指向的位置)。如果把一个字符串字面量拷贝给一个数组,就可以随意改变数据,除非把数组声明为const。

       总之,初始化数组把静态存储区的字符串拷贝到数组中,而初始化指针只把字符串的地址拷贝给指针。

示例

  1. //Visual Studio中加上这句才可以使用scanf()
  2. //否则只能使用scanf_s()
  3. #define _CRT_SECURE_NO_WARNINGS
  4. #include <stdio.h>
  5. #include <stdbool.h>
  6.  
  7. #define MSG "I'm special"
  8.  
  9. //argc: 参数个数 argv[]: 参数数组
  10. int main(int argc, char *argv[])
  11. {
  12. char ar[] = MSG;
  13. const char *pt = MSG;
  14. printf("address of \"I'm special\": %p \n", "I'm special");
  15. printf(" address ar: %p\n", ar);
  16. printf(" address pt: %p\n", pt);
  17. printf("address of MSG: %p\n", MSG);
  18. printf("address of \"I'm special\": %p \n", "I'm special");
  19.  
  20. system("pause");
  21. return 0;
  22. }

运行测试
1111.png
打印结果说明:
1、pt和MSG地址相同,说明pt指向静态存储区的I'm special
2、ar和MSG地址不同,说明ar存储的是I'm special的副本
3、I’m special字符串在程序中出现了多次,但编译器只使用了一个存储位置(因使用的编译器而异)
4、静态数据使用的内存与ar使用的动态内存不同,不仅值不同,特定编译器甚至使用不同的位数表示两种内存

示例二:数组与指针的区别

  1. //Visual Studio中加上这句才可以使用scanf()
  2. //否则只能使用scanf_s()
  3. #define _CRT_SECURE_NO_WARNINGS
  4. #include <stdio.h>
  5. #include <stdbool.h>
  6.  
  7. //argc: 参数个数 argv[]: 参数数组
  8. int main(int argc, char *argv[])
  9. {
  10. //数组名heart是常量
  11. char heart[] = "I love Tillie!";
  12. //指针名heard是变量
  13. const char *head = "I love Millie!";
  14.  
  15. //两者都可以使用数组表示法
  16. for (int i = 0; i < 6; i++)
  17. putchar(heart[i]);
  18. putchar('\n');
  19. for (int i = 0; i < 6; i++)
  20. putchar(head[i]);
  21. putchar('\n');
  22.  
  23. //两者都能进行指针加法操作
  24. for (int i = 0; i < 6; i++)
  25. putchar(*(heart + i));
  26. putchar('\n');
  27. for (int i = 0; i < 6; i++)
  28. putchar(*(head + i));
  29. putchar('\n');
  30.  
  31. //只有指针表示法可以进行递增操作
  32. while (*(head) != '\0') //在字符串末尾处停止
  33. putchar(*(head++));//打印字符,指针指向下一个位置
  34. putchar('\n');
  35. //可以将数组赋给指针
  36. //head = heart;
  37. //不能将指针赋给数组,因为赋值运算的左侧必须是变量,
  38. //或者是可修改的左值,如*pt_int。
  39. //heart = head;//非法
  40.  
  41. //两者都可改变数组中元素的信息
  42. //下面两句等效
  43. heart[7] = 'M';
  44. *(heart + 7) = 'M';
  45.  
  46. //数组的元素是变量(除非数组被声明为const),但是数组名不是变量。
  47. //未使用const限定符的指针初始化
  48. char * word = "frame";
  49. //编译器可能允许这样做,但是对当前的C标准而言,这样的行为是
  50. //未定义的。因为指针指向的是存储在静态区中的原始字符串,可能
  51. //会影响其他使用了这个字符串的地方。
  52. //word[1] = 'l';//未定义的行为,可能导致程序异常
  53. //因此,建议在把指针初始化为字符串字面量时使用const限定符
  54. const char * p1 = "Klingon"; //推荐用法
  55. //然而,把非const数组初始化为字符串字面量却不会导致类似问题。
  56. //因为数组获得的是原始字符串的副本。
  57.  
  58.  
  59. system("pause");
  60. return 0;
  61. }

运行测试

1111.png

示例:字符串数组

  1. //Visual Studio中加上这句才可以使用scanf()
  2. //否则只能使用scanf_s()
  3. #define _CRT_SECURE_NO_WARNINGS
  4. #include <stdio.h>
  5. #include <stdbool.h>
  6.  
  7. #define SLEN 40
  8. #define LIM 5
  9.  
  10. //argc: 参数个数 argv[]: 参数数组
  11. int main(int argc, char *argv[])
  12. {
  13. //内含5个指针的数组
  14. //指针所指向的字符串字面量储存在静态内存中
  15. const char *mytalents[LIM] = {
  16. "Adding numbers swiftly",
  17. "Multiplying accurately",
  18. "Stashing data",
  19. "Following instructions to the letter",
  20. "Understanding the Clanguage"
  21. };
  22. //内含5个数组的数组,每个数组内含40个char类型的值,共占200字节。
  23. //子数组储存的是字符串字面量的副本
  24. char yourtalents[LIM][SLEN] = {
  25. "Walking in a straight line",
  26. "Sleeping",
  27. "Watching television",
  28. "Mailing letters",
  29. "Reading email"
  30. };
  31. int i;
  32.  
  33. puts("Let's compare talents.");
  34. printf("%-36s %-25s\n", "My Talents", "Your Talents");
  35. for (i = 0; i < LIM; i++)
  36. printf("%-36s %-25s\n", mytalents[i], yourtalents[i]);
  37. printf("\nsizeof mytalents: %zd, sizeof yourtalents: %zd\n",
  38. sizeof(mytalents), sizeof(yourtalents));
  39.  
  40. /*
  41. 总之,如果不需要更改字符串字面量,推荐使用指针表示法(效率高).
  42. 如果需要更改,则使用数组表示法。
  43. */
  44.  
  45. system("pause");
  46. return 0;
  47. }

运行测试
1111.png

示例:指针和字符串

  1. //Visual Studio中加上这句才可以使用scanf()
  2. //否则只能使用scanf_s()
  3. #define _CRT_SECURE_NO_WARNINGS
  4. #include <stdio.h>
  5. #include <stdbool.h>
  6.  
  7. //argc: 参数个数 argv[]: 参数数组
  8. int main(int argc, char *argv[])
  9. {
  10. const char * mesg = "Don't be a fool!";
  11. const char * copy;
  12. //让copy指向mesg指向的字符串地址
  13. copy = mesg;
  14. printf("%s\n", copy);
  15. //如果编译器不识别%p,用%u或%lu代替%p
  16. printf("mesg = %s; &mesg = %p; value = %p\n", mesg, &mesg, mesg);
  17. printf("copy = %s; &copy = %p; value = %p\n", copy, &copy, copy);
  18.  
  19. system("pause");
  20. return 0;
  21. }

运行测试
1111.png


标签: C语言

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号