1.关于以下代码,正确的说法是
char acX[] = "abc";
char acY[] = { 'a', 'b', 'c' };
char *szX = "abc";
char *szY = "abc";
A acX和acY都是数组,当然可以修改
B 因为"abc"是常量字符串,当它被多次使用时,编译器并不愿意再多分配出额外的内存空间存放多个"abc",而是重复使用这一个"abc",所以,scX和scY指针会指向同一个地址。
C 因为acX是字符串数组,字符串的尾部有一个结束符'\0',所以acX有四个元素,内存空间比acY大
D 字符指针指向的是常量字符串,常量字符串不能修改.
2.运行以下C语言代码,输出的结果是 stra tra ra
int main()
{
char *str[3] = { "stra", "strb", "strc" };
char *p = str[0];
int i = 0;
while (i<3)
{
printf("%s\n", p++);//stra tra ra
i++;
}
system("pause");
return 0;
}
创建了一个指针数组str,它的每个数组元素相当于一个指针变量,也就是说str数组中每个元素存放的是字符串的地址。str[0]存放的是第一个字符串的地址,赋值给p,p是一个指向char型元素的指针,printf输出遇到空字符停止
a,p是char*类型,每次++,后移一位(char)
b,char *p=str[0]相当于char *p="stra",p先指向s,p++后,指向t
c,printf输出遇到空字符停止
3.设 int x[] = { 1, 2, 3, 4, 5, 6 },*p=x; 则值为3的表达式是 p += 2; *p++;
int x[] = { 1, 2, 3, 4, 5, 6 },*p=x;
p += 2;
printf("%d\n", *p++);
4. 以下关于指针的说法,正确的是 (C)
A. int *const p与int const *p等价
B. const int *p与int *const p等价
C. const int *p与int const *p等价
D. int *p[10]与int (*p)[10]等价
int *const p是指向整数数据的常量指针,指针不可再被修改。
int const *p 是指向数据类型为常量的指针p,p所指向的数据值不可更改。
const int *p与int const *p等价
int *p[10]与int (*p)[10]不等价 前者是一个数组,数组里存放十个这样的指向整形数据的指针,后者是一个数组指针,p指向存放整形数据的数组
最简单的方法,把int去掉,从右往左看 ,直接看const在*的那一侧,同一侧就是等价,不同侧就不等价。
4. 如下代码输出结果 未知
char *myString()
{
char buffer[6] = { 0 };
char *s = "Hello World!";
for (int i = 0; i < sizeof(buffer)-1; i++)
{
buffer[i] = *(s + i);
}
//printf("%d\n", sizeof(buffer));6
return buffer;
}
int main()
{
printf("%s\n", myString());
system("pause");
return 0;
}
函数char *myString()中没有使用new或者malloc分配内存,所有buffer数组的内存区域在栈区随着char *myString()的结束,栈区内存释放,字符数组也就不存在了,所以会产生野指针,输出结果未知 。
局部数组,具有局部作用域,当函数调用结束之后,数组也就被操作系统销毁了,即回收了他的内存空间,它里面的东西随时都有可能被覆盖。虽然此时我们获得了指向这一块内存的指针,但此刻里面的东西很大可能都不是我们想要的了。
a: 返回一个静态的数组
b: 动态分配内存
c: 使用全局数组
5.下面的代码输出 4,1
int main()
{
int i = 1;
printf("%d,%d\n", sizeof(i++), i);//4,1
system("pause");
return 0;
}
i++还是int类型,跟数据是多少没有关系,所以最后结果为4(int无论在32位还是64位都是占4字节)。如果sizeof的操作数是一个表达式的话,这个表达式时不会被计算的。
sizeof当预处理看就行了,它后面括号里的东西,根本不求值,只根据C的一堆规则判断结果类型,然后返回结果类型的大小。另外一个操作符typeid也是如此。
6.下面代码输出 255
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = (char)(-1 - i);
}
printf("%d\n", strlen(a));//255
system("pause");
return 0;
}
-128
原 1000 0000, 0000 0000, 0000 0000, 1000 0000
反 1111 1111, 1111 1111, 1111 1111, 0111 1111
补 1111 1111, 1111 1111, 1111 1111, 1000 0000
-129
原 1000 0000, 0000 0000, 0000 0000, 1000 0001
反 1111 1111, 1111 1111, 1111 1111, 0111 1110
补 1111 1111, 1111 1111, 1111 1111, 0111 1111
-255
原 1000 0000, 0000 0000, 0000 0000, 1111 1111
反 1111 1111, 1111 1111, 1111 1111, 0000 0000
补 1111 1111, 1111 1111, 1111 1111, 0000 0001
-256
原 1000 0000, 0000 0000, 0000 0001, 0000 0000
反 1111 1111, 1111 1111, 1111 1110, 1111 1111
补 1111 1111, 1111 1111, 1111 1111, 0000 0000
for 循环内,当i 的值为0 时,a[0]的值为-1。关键就是-1 在内存里面如何存储。
我们知道在计算机系统中,数值一律用补码来表示(存储)。主要原因是使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。正数的补码与其原码一致;负数的补码:符号位为1,其余位为该数绝对值的原码按位取反,然后整个数加1。
按照负数补码的规则,可以知道-1 的补码为0xff,-2 的补码为0xfe……当i 的值为127时,a[127]的值为-128,而-128 是char 类型数据能表示的最小的负数。当i 继续增加,a[128]的值肯定不能是-129。因为这时候发生了溢出,-129 需要9 位才能存储下来,而char 类型
数据只有8 位,所以最高位被丢弃。剩下的8 位是原来9 位补码的低8 位的值,即0x7f。当i 继续增加到255 的时候,-256 的补码的低8 位为0。然后当i 增加到256 时,-257 的补码的低8 位全为1,即低八位的补码为0xff,如此又开始一轮新的循环……
按照上面的分析,a[0]到a[254]里面的值都不为0,而a[255]的值为0。strlen 函数是计算字符串长度的,并不包含字符串最后的‘\0 ’。而判断一个字符串是否结束的标志就是看是否遇到‘\0 ’。如果遇到‘\0 ’,则认为本字符串结束。
分析到这里,strlen(a)的值为255 应该完全能理解了。这个问题的关键就是要明白char类型默认情况下是有符号的,其表示的值的范围为[-128,127],超出这个范围的值会产生溢出。另外还要清楚的就是负数的补码怎么表示。弄明白了这两点,这个问题其实就很简单了。