数组作为函数的参数:
我们都知道数组名其实就是指向数组第一个元素的指针(即&array[0]), 将数组名传递给函数的是该指针的一份拷贝。 如果在函数中执行了下标引用, 实际就是对这个指针进行了间接访问操作, 通过这种操作可以访问和修改调用程序的数组元素。
如何理解数组名的传入方式呢?(传值 or 传址)
Q:什么是传址调用?
A:传址调用:通过传递一个指向所需元素的指针, 然后在函数中对该指针执行间接访问操作实现对数据的访问。
其实对于所有的传递给函数的参数都是通过传值方式进行的, 但数组名参数的行为却仿佛是通过传址方式调用的。
Q:那么数组的传值调用表现在什么地方呢?
A:传递给函数参数的是指向数组起始位置的指针的一份拷贝, 所以函数可以自由操作它的指针形参, 而不必担心会修改对应的作为实参的值。 但是若是执行了间接访问操作, 那么函数就可以修改那个变量。 但是无论函数对参数(指针)如何进行修改, 都不会修改调用程序的指针实参本身(但可能改变它所指的内容)
# include <stdio.h>
void demo(int *c)
{
int d[5]= {45, 56};
c = d;
}
int main(void)
{
int a[10] = {1,2 ,4, 5, 6};
int * b;
int i;
b = a;
demo(b);
for (i = 0; i < 10; i++)
{
printf ("%d", b[i]);
}
return 0;
}
对一个不打算改变形参的函数, 将形参声明为const, 有三个好处:
第一:这是一个良好的文档习惯
第二:编译器可以捕捉到任何试图修改数据的意外error
第三:这类声明允许向函数传递const参数
初始化
存储于静态内存的数组只初始化一次。 在程序开始之前。 这个初始化是由链接器完成的, 如果数组没有初始化, 则自动设置为零。
存储于动态内存的数组, 因为自动变量位于运行时堆栈中, 执行流每次进入他们所在的代码块时, 这类变量每次所处的内存位置可能并不相同。 在程序开始之前, 编译器没办法对这些位置进行初始化。 所以缺省情况下是未初始化的。 如果自动变量的声明中给出了初始化, 每次当执行自动变量声明所在的作用域时, 变量就被一条隐式的赋值语句初始化。 这条赋值语句和普通的赋值语句一样需要时间和空间来执行。
弄清楚下面三个的区别:
char string[6]=’h’,’e’,’l’,’l’,’o’,’\0’;
char string[] = “hello”
char *string = “hello”
其中第一个与第二个是等价的, 第三个表示string指向字符串常量”hello”的第一个字符。 注意第二个中的”hello”并不是一个字符串常量, 但是除了这种情况之外的其余情况, “hello”都表示字符串常量。
指向数组的指针
int vector[10], * p1 = vector; // yes
int mat[3][10], * p2 = mat; // error
Vector 是一个指向数组的第一个元素的指针;p1指向的也是一个元素, 所以成立
mat是一个指向数组的指针;p2指向的是一个元素, 所以不成立。 正确的声明应该为
int (*p)[10]
下标的优先级高于间接访问操作符, 凡是括号的存在, p 是一个指针。 p 指向的是某种类型的数组。 该数组元素都为整数。 p 是一个指向拥有10个元素的数组的指针。
# include <stdio.h>
int main(void)
{
int mat[3][10] = {{1, 2, 3, 4, 5}, {11, 22, 33, 44, 55}, {111, 222, 333, 444, 555}};
int (*p)[10] = mat;
int i;
for (i = 0; i < 3; i++)
{
printf ("%d", *p[i]);
}
return 0;
}
/*
在code::blocks中输出结果是:1 11 111
*/
函数接收二维数组。 就是指向数组的指针。
int fun2(int (*P)[10]); // 这两个是一个意思, 接收一个二维数组, 指向数组的指针。
int fun2(int p[][10]);
指针数组
int *pi[10];
定义一个指针数组类似这样。 由于下标的引用高于间接访问, 所以在这个表达式中, 首先执行下标。 所以pi是一个元素为 10 的数组。 下标引用执行完之后,执行间接访问操作, 其结果必然为一个整数(对一个一维数组使用间接访问操作符结果必然为一个确定的值)。
指针数组说白了就是数组中每一个元素都是一个指针
一个指针数组的例子:
char const *keyword[] = {
"do",
"for",
"if",
"register",
"return",
"switch",
"while",
NULL
};
# include <stdio.h>
int main(void)
{
char const *keyword[] = {
"do",
"for",
"if",
"register",
"return",
"switch",
"while",
NULL
};
printf ("%s", keyword[1]);
return 0;
}
/*
在code::blocks中输出结果是:for
*/
数组总结
数组名在绝大多数时候表示的是指向数组第一个元素的指针, 有两个例外:sizeof返回的是整个数组所占的字节数, &返回一个指向数组的指针, 而不是指向数组第一个元素的指针的指针。
下标表达式与间接访问表达式是一致的。 指针表达式的效率可能比下标表达式高, 但是下标表达式的效率不可能比指针表达式的效率高。
数组和指针并不相等, 当我们定一个数组时, 它分配了一些内存空间, 用于容纳数组元素。 但是定义一个指针时, 只分配了用于容纳指针本身的空间。
当数组名作为函数参数传递时, 实际传递给函数的是一个指向数组第一个元素的指针。 函数接收的只是元参数的一份拷贝, 所以函数可以对其进行操作而不影响实际参数。 但是如果进行间接访问操作就会影响到原来的数组。
多维数组是一维数组的一种特型, 就是它的每个元素是个数组。 多维数组根据行主序存储, 也就是最右边的下标最先变化, 多维数组名是一个指向它第一个元素的指针, 也就是一个指向数组的指针。 使用多维数组作为函数参数时, 除了第一维可以省略, 其余都不能省略。
指针数组:数组的每一个元素都是指针的数组。 字符串的列表可以以矩阵的形式存储, 也可以以指向字符串常量的形式进行存储。 如果是矩阵存储, 那么每行必须与最长的一行相同。