关于C中的const和字符串常量,求讲解

lengleng2008 2010-10-27 09:44:55
1、当函数使用指针参数,且使用常变量const修饰时,const保护的是形参所指向的变量,而不是形参(指针)本身,这是为什么?
例子:(其中的函数摘自《从问题到程序》)
#include <stdio.h>

int strLength (const char *s) {
int n = 0;
while (*s != '\0') {
++s;
++n;
}
return n;
}

int main () {
char a[] = "hahaha";

printf("length: %d\n", strLength(a));

getchar();
return 0;
}

这个程序可以跑通,也就是说在函数里,指针s是可以指向数组的下一个元素的,意味着s的值(即地址)可以更新,const只是保护s指向的字符数组a不被更新。
按我的理解,const在变量定义中保护定义的变量本身。如果定义的是指针变量,则我以为该指针变量的值(即数组的首地址)初始化后不能改变,但事实是该指针变量仍然可以更新,而被指变量不能更新。还请达人讲讲其中道理。

2、有如下两个变量定义:
char *p = "kakaka" 和
char s[] = "kakaka",
系统在执行前者的过程中,是先定义了一个字符串常量,之后用指针p指向它,而字符串常量是常量,故不可被更新,如语句*(p + 1) = 'h';是错误的语句,不会得到想要的结果;
而系统在执行后者的过程中,并没有定义字符串常量,只是一个字符数组,故该"kakaka"中的某单个字符是可以更改的。
我想问字符串常量到底是什么?为何有上述现象出现?求高手讲解,谢谢!!

...全文
366 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
王大军9527 2010-10-28
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 pengzhixi 的回复:]
1、当函数使用指针参数,且使用常变量const修饰时,const保护的是形参所指向的变量,而不是形参(指针)本身,这是为什么?

涉及到const 修饰的对象到底是指针本身还是指针所指对象。
const *ptr;//修饰的是指针所指对象
*const ptr;//修饰的是指针本身。

2.规定而已。
char *p = "kakaka" //存放在常量区
char s[] = "……
[/Quote]

正解!

至于const修饰的是常量还是变量?我们来看一个题目:


#include <stdio.h>
int main()
{
int a[3] = {0};
const int tmp = 0; /* const修饰tmp,tmp是放在静态数据区中.rodata中还是栈呢?
* 若在.rodata中,就是我们所说的常量了,不能更改,字符串常量就是在这
* 若在栈上,那说明可以修改的,但与const修饰有矛盾!
*/

a[3] = 10; /* 数组溢出赋值到数组的下个地址空间了 */
printf("tmp: %d\n", tmp); /* tmp值改变啦
* 说明tmp是在栈的,const的没能对他进行保护
*/

return 0;

}

/*
* 总结下:
* const这个修饰词只是告诉编译器这个变量不能被修改,如果在编译期间修改了变量,
* 编译器会发现错误,给出提示,
* 而在程序运行中通过其他方法修改了const修饰的变量,编译器是无能为力的
*
* 所以const的是给编译器看的,
* 字符串常量用const修饰时 const char *p = "Hello";
* 因为字符串常量是在.rodata(也可能.text)中,所以其本身就是不能被修改的,
* const修饰下是又让编译器来保护下。
*
*/

bo_00 2010-10-28
  • 打赏
  • 举报
回复
再补充一下:接9楼

int * const p;
意思是 p下存放的0x2222的这个值不可变,至于0x2222地址下的5,是可变的。

int const * const a;
意思是,p下存放的0x2222的这个值不可变,至于0x2222地址下的5,也是不可变的。
bo_00 2010-10-28
  • 打赏
  • 举报
回复
补充一下;

const int *p;

例如
1. 指针变量p 存放在内存的0x1111地址下。
2. 0x1111这个地址下,存放的是*p指向的数据常量的内存地址,0x2222
3. 0x2222下存放的数据常量例如是5。

含义是,5不能被改变为其他。
至于0x1111或0x2222就看系统怎么分配的了,每次程序运行时,不会老是老是0x1111和0x2222
try325 2010-10-28
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 pengzhixi 的回复:]

1、当函数使用指针参数,且使用常变量const修饰时,const保护的是形参所指向的变量,而不是形参(指针)本身,这是为什么?

涉及到const 修饰的对象到底是指针本身还是指针所指对象。
const *ptr;//修饰的是指针所指对象
*const ptr;//修饰的是指针本身。

2.规定而已。
char *p = "kakaka" //存放在常量区
char s[] = ……
[/Quote]
正解
bo_00 2010-10-28
  • 打赏
  • 举报
回复
const本来就是保护 常量的。不是保护变量的,变量本身就是可变的量。

const char *p;
*p指向的地址空间存放的常量,是不可变的。
至于变量p是指针变量,存放的是地址值,存放的地址值是可变的,(也就是说,指向的地址下的常量不变,但是这个常量放在内存的什么地方可变根据系统分配随意)

又如
const int i;整型变量i。
变量i的地址是 &i,这个值根据系统的分配,不会总是在一个地址下。
但&i这个地址下,存放的值,是不变的。(不管系统把i的值放在哪个地址下,这个地址下存放的值是不变的)
lengleng2008 2010-10-28
  • 打赏
  • 举报
回复
多谢各位高手!!长知识啊。。
pengzhixi 2010-10-27
  • 打赏
  • 举报
回复
1、当函数使用指针参数,且使用常变量const修饰时,const保护的是形参所指向的变量,而不是形参(指针)本身,这是为什么?

涉及到const 修饰的对象到底是指针本身还是指针所指对象。
const *ptr;//修饰的是指针所指对象
*const ptr;//修饰的是指针本身。

2.规定而已。
char *p = "kakaka" //存放在常量区
char s[] = "kakaka",//存放在栈区或者全局变量区。
a919457497 2010-10-27
  • 打赏
  • 举报
回复
字符串常量 放在常量区,首地址赋给指针
后者是分配一个数组空间,然后用字符序列填充
数组名与字符串指针都是地址,但有区别。。

个人理解,不知道对不对,等待高手吧
無_1024 2010-10-27
  • 打赏
  • 举报
回复
我认为是 char s[] = "kakaka"只是一个变量的定义
而C++变量可以多次声明 但是只能定义一次
定义时已经为变量分配存储空间,而在此还指定了初值
lengleng2008 2010-10-27
  • 打赏
  • 举报
回复
谢谢回复!
可是按照书中的解释,char *p = "kakaka"; 在系统执行时是先建立一个字符串常量再把指针指到它上面去的,并不是把这句前头加个const
我就是想问问为什么会这样,而char s[] = "kakaka"为啥就没有这一步……
ruanxuewu0120 2010-10-27
  • 打赏
  • 举报
回复
const char *p1和char const *p2和char *const p3有什么区别?
lovesi3344 2010-10-27
  • 打赏
  • 举报
回复
char *p = "kakaka" ;
必须这样理解:const char *p = "kakaka" ;
这就是传说中的字符串常量


char s[] = "kakaka";
要区别于 const char s[] = "kakaka";
通常建议const数组,程序具有更好的健壮性
《明解C语言 第3版 入门篇》是日本的C语言经典教材,自出版以来不断重印、修订,被誉为“C语言圣经”。 《明解C语言 第3版 入门篇》图文并茂,示例丰富,第3版从190段代码和164幅图表增加到205段代码和220幅图表,对C语言的基础知识进行了彻底剖析,内容涉及数组、函数、指针、文件操作等。对于C语言语法以及一些难以理解的概念,均以精心绘制的示意图,清晰、通俗地进行讲解。原著在日本广受欢迎,始终位于网上书店C语言著作排行榜首位。 第1章 初识C语言 1 1-1 显示计算结果 2 计算整数的和并显示结果 2 程序和编译 2 注释 3 固定代码 4 printf函数:格式化输出函数 4 语句 5 计算并显示整数的差 5 格式化字符串和转换说明 6 符号的称呼 8 无格式化输出 8 字符串常量 10 转义字符 10 1-2 变量 11 变量和声明 11 赋值 12 初始化 13 声明时初始化 14 初始化和赋值 14 1-3 输入和显示 16 通过键盘进行输入 16 格式化输入函数scanf 16 乘法运算 17 输出函数puts 18 总结 21 第2章 运算和数据类型 23 2-1 运算 24 运算符和操作数 24 乘除运算符和加减运算符 25 除法运算的商和余数 25 使用printf函数打印% 26 获取整数的最后一位数字 26 多个转换说明 27 单目运算符 28 赋值运算符 29 表达式和赋值表达式 30 表达式语句 30 2-2 数据类型 31 平均值 31 数据类型 32 数据类型和对象 33 整型常量和浮点型常量 34 double类型的运算 34 数据类型和运算 35 类型转换 37 转换说明 39 总结 42 第3章 分支结构程序 45 3-1 if语句 46 if语句·其1 46 奇数的判定 47 if语句·其2 48 奇数·偶数的判断 49 非0的判断 49 if语句的结构图 50 相等运算符 52 余数的判断 53 关系运算符 54 嵌套的if语句 55 判断 57 计算较大值 58 计算三个数的最大值 59 条件运算符 60 差值计算 61 复合语句(程序块) 62 逻辑运算符 65 短路值 67 3-2 switch语句 70 switch语句和break语句 70 复杂的switch语句 72 switch语句和if语句 73 选择语句 74 总结 75 第4章 程序的循环控制 77 4-1 do语句 78 do语句 78 复合语句(程序块)的声明 79 读取一定范围内的值 80 逻辑非运算符·德摩根定律 81 德摩根定律 81 多个整数的和及平均值 82 复合赋值运算符 84 后置递增运算符和后置递减运算符 85 4-2 while语句 87 while语句 87 用递减运算符简化程序代码 88 数据递增 90 限定次数的循环操作 91 字符常量和putchar函数 92 do语句和while语句 93 前置递增运算符和前置递减运算符 93 do语句的显示 95 逆向显示整数值 96 4-3 for语句 99 for语句 99 使用for语句实现固定次数的循环 101 偶数的枚举 103 约数的枚举 104 表达式语句和空语句 104 循环语句 105 4-4 多重循环 107 二重循环 107 用break语句强制结束循环 108 显示图形 109 多重循环 111 4-5 程序的组成元素和格式 114 关键字 114 运算符 114 标识符 114 分隔符 115 常量字符串常量 115 自由的书写格式 116 连接相邻的字符串常量 117 缩进 117 总结 118 第5章 数组 121 5-1 数组 122 数组 122 数组的声明(使用数组前的准备) 123 访问数组(数组的使用方法) 123 数组的遍历 124 数组初始化 126 数组的复制 127 输入数组元素的值 129 对数组的元素进行倒序排列 129 使用数组进行成绩处理 131 对象式宏 131 数组元素的最大值和最小值 133 赋值表达式的判断 134 数组的元素个数 135 5-2 多维数组 138 多维数组 138 总结 142 第6章 函数 145 6-1 什么是函数 146 main函数和库函数 146 什么是函数 146 函数定义 147 函数调用 148 三个数的最大值 151 将函数的返回值作为参数传递给函数 152 调用其他函数 154 值传递 155 6-2 函数设计 158 没有返回值的函数 158 通用性 159 不含形参的函数 160 函数返回值的初始化 161 作用域 161 文件作用域 162 声明和定义 163 函数原型声明 163 头文件和文件包含指令 164 函数的通用性 165 数组的传递 166 函数的传递和const类型的修饰符 168 线性查找(顺序查找) 170 哨兵查找法 172 多维数组的传递 175 6-3 作用域和存储期 178 作用域和标识符的可见性 178 存储期 180 总结 185 第7章 基本数据类型 189 7-1 基本数据类型和数 190 算数类型和基本数据类型 190 基数 191 基数转换 192 7-2 整型和字符型 195 整型和字符型 195 头文件 196 字符型 199 位和CHAR_BIT 200 sizeof运算符 200 size_t型和typedef声明 202 整型的灵活运用 202 整型的内部表示 204 无符号整数的内部表示 205 有符号整数的内部表示 207 按位操作的逻辑运算 209 位移运算符 212 整型常量 216 整型常量的数据类型 218 整数的显示 218 数据溢出和异常 220 7-3 浮点型 221 浮点型 221 浮点型常量 223 头文件 224 循环的控制 225 7-4 运算和运算符 228 运算符的优先级和结合性 228 优先级 228 结合性 228 数据类型转换 230 总结 232 第8章 动手编写各种程序吧 235 8-1 函数式宏 236 函数和数据类型 236 函数式宏 237 函数和函数式宏 238 不带参数的函数式宏 239 函数式宏和逗号运算符 240 8-2 排序 243 冒泡排序法 243 8-3 枚举类型 246 枚举类型 246 枚举常量 248 命名空间 250 8-4 递归函数 251 函数和类型 251 阶乘 252 8-5 输入输出和字符 255 getchar函数和EOF 255 从输入复制到输出 256 数字字符计数 256 字符 258 转义字符 261 总结 263 第9章 字符串的基本知识 265 9-1 什么是字符串 266 字符串字面量 266 字符串字面量的长度 266 字符串 268 字符数组的初始化赋值 269 空字符串 270 字符串的读取 270 格式化显示字符串 271 9-2 字符串数组 273 字符串数组 273 读取字符串数组字符串 274 9-3 字符串处理 275 字符串长度 275 显示字符串 277 数字字符的出现次数 278 大小写字符转换 279 字符串数组的参数传递 280 总结 283 第10章 指针 285 10-1 指针 286 函数的参数 286 对象和地址 287 取址运算符 288 指针 289 指针运算符 291 10-2 指针和函数 293 作为函数参数的指针 293 计算和与差 294 二值互换 296 将两个值排序 297 scanf函数和指针 298 指针的类型 299 空指针 300 标量型 301 10-3 指针和数组 302 指针和数组 302 指针运算符和下标运算符 304 数组和指针的不同点 306 数组的传递 308 总结 311 第11章 字符串和指针 315 11-1 字符串和指针 316 用数组实现的字符串和用指针实现的字符串 316 用数组实现的字符串和用指针实现的字符串的不同点 318 字符串数组 320 11-2 通过指针操作字符串 323 判断字符串长度 323 字符串的复制 325 不正确的字符串复制 328 返回指针的函数 329 11-3 字符串处理库函数 330 strlen函数:字符串的长度 330 strcpy函数、strncpy函数:复制字符串 331 strcat函数、strncat函数:连接字符串 332 strcmp函数、strncmp函数:比较字符串的大小关系 332 atoi函数、atol函数、atof函数:转换字符串 333 总结 336 第12章 结构体 339 12-1 结构体 340 数据关联性 340 结构体 342 结构体成员和运算符 344 成员的初始化 345 结构体成员和->运算符 346 结构体和typedef 348 结构体和程序 350 聚合类型 351 返回结构体的函数 351 命名空间 353 结构体数组 353 派生类型 355 12-2 作为成员的结构体 356 表示坐标的结构体 356 具有结构体成员的结构体 357 总结 361 第13章 文件处理 363 13-1 文件与流 364 文件与流 364 标准流 365 FILE型 365 打开文件 365 关闭文件 368 打开与关闭文件示例 369 文件数据汇总 370 写入日期和时间 372 获取上一次运行时的信息 376 显示文件内容 378 文件的复制 380 13-2 文本和二进制 382 在文本文件保存实数 382 文本文件和二进制文件 383 在二进制文件保存实数 384 显示文件自身 386 13-3 printf函数与scanf函数 389 printf函数:带格式输出 389 scanf函数:带格式的输入 393 总结 397 附录 C语言简介 399 C语言的历史 400 K&R——C语言的圣经 400 C语言标准规范 400 结语 402 参考文献 405

69,382

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧