1.分析下面代码有什么问题?
void test1()
{
char string[10];
char* str1 = "0123456789";
strcpy( string, str1 );
}
解:字符串str1需要11个字节才能存放下(包括末尾的’\0’),而string只有10个字节的空间,strcpy会导致数组越界;
代码本身没有错误,如果将str1拷贝到string中,string的长度会增加,并且也可以正确的输出string。(c、java代码都测试过,没有问题)
但是存在潜在的危险,string定义的长度是10,而拷入str1之后string的长度增为11,也就是说,在内存中,如果紧接string之后有内容,将被覆盖,这样会导致string之后的内存存取错误。
知识点:
允许用字符串字面值对字符数组初始化,\0也会拷贝进去。
编译器会自动把数组名字替换为指向数组首元素的指针。(详细看下1)
strcpy为c风格字符串的函数,定义在头文件cstring中,strcpy(p1,p2)将p2拷贝给p1,返回p1,连字符串结束标志\0也一起copy。
存在问题:
string不能用作变量名,string是c++中的标准库类型。
str1是一个指向字符的指针/字符数组,字符串字面值"0123456789"的长度为11,strcpy函数将str1拷贝给string,但string大小为10,str大小为11,导致数组越界。
补充:
1.C/C++里面的数组名字会退化为指针,所以数组名str1实际指的是数组的第一个元素的地址。而数组名作为指针来讲有特殊性,它正在它所指向的内存区域中,&str1的值和str1的数值是相同的。
2.strcpy函数的实现是以’\0’判定拷贝结束的,并且会给目标字符串结尾加一个’\0’
3.先定义一个一维数组
int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
一、数组名是什么
数组名经常被隐式转换为指向数组首元素的指针常量:
数组名不是指针,但大多数使用到数组名的地方,编译器都会把数组名隐式转换成一个指向数组首元素的指针来处理。只有两种情况下例外:
1.对数组名使用sizeof运算符
sizeof(a)
这将会得到整个数组所占的内存大小,a是长度为10的int(4字节)数组,运算结果是40。此时a代表的是数组所占用的那一整块内存。
2.对数组名取地址
&a
运算结果是数组的地址。注意,数组的地址和数组首元素的地址是不同的概念,尽管二者存储的值是相同的,但它们的跨度是不同的,不信的话,对它们进行解引用或比较一下a+1和&a+1,你就会明白了。
二、下标引用
除了上面说的两种例外,其他情况下编译器都将数组名隐式转换成指针常量。比如使用下标引用数组元素:
a[3] // 自动转换成下面的表达式
*(a + 3)
a的值被转换成指针常量,指向第一个元素,向右移动3 * sizeof(int)个字节,然后解引用,便得到了第4个元素的内容。
因为第一种写法会自动转换成第二种,这个过程需要一些开销,所以我们说第二种写法通常效率会高一些。
三、数组名的类型
以数组名a为例,a的类型是:
int * const a;
数组的类型取决于数组元素的类型:如果它们是int类型,那么数组名的类型就是“指向int的指针常量”;如果它们是其他类型,那么数组名的类型就是“指向其他类型的指针常量”。(出自《C和指针》第141页)
这里需要补充两点,&a的类型和二维数组名的类型。
在接下来的第四点会详细解释&a的含义,这里先给出结论,&a是指向数组的指针,而&a的类型是int ()[10]。
然后二维数组的类型同样取决于数组元素的类型,假设有二维数组int b[10][20]
因为C语言的多维数组实际上是一维数组,二维数组实际上只是一个一维数组,只不过里面每个元素又是一个一维数组而已。所以b的类型是int ()[20],而&b的类型是int (*)[10][20]
四、a 和 &a
一个有趣的事实是,a 和 &a 的值是相同的。
**&a 中的a代表整个数组,而不是指向数组首元素的指针常量。 **
“取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量值的指针”(出自《C和指针》第142页)。
通过四条语句可以更好地理解它们的关系:
我知道%p用来输出地址,但为了方便观察我改用%d以整数10进制形式输出
printf("a = %d\n", a); //19920908
printf("a + 1 = %d\n", a + 1); //19920912
printf("&a = %d\n", &a); //19920908
printf("&a + 1 = %d\n", &a + 1); //19920948
可以很直观地看出a和&a的区别了。
a指向首元素,右移一位,地址增加了4字节,也就是一个int的长度;
&a指向数组,右移一位,地址增加了40字节,相当于指向了下一个数组(可能并不存在),这在C++里称为尾后指针。
五、获取数组元素个数
用数组的总字节长度除以单个元素的字节长度得到元素个数
sizeof(a) / sizeof(*a)