C语言学习之数组第一讲
2016-01-16 09:17
344 查看
一、数组
1.一维数组
1.1数组名
int a;
int b[10];
上面变量a称为标量,因为它是单一的值,这个变量的类型是一个整数。变量b成为数组,而不是标量因为踏实一些值得集合。下标和数组名仪器使用,用于标识该集合中某个特定的值。例如b{0}表示数组b的第一个值,而每个特定的值都是一个标量,可用于使用标量数据的上下文环境中。
b[0]的类型是整型,b表示什么呢?b是一个指针常量,是数组第一个元素的地址,它的类型取决于数组元素类型,这里即为int型。
注意这个值是指针常量,而不是指针变量。你不能改变常量的值。在程序完成链接之后,内存中的数组的位置是固定的,所以,数组名的值是一个之真敞亮。
两种场合下数组名并不是指针常量来表示。1.数组名作为sizeof操作符或者弹幕操作符&的操作数时。sizeof返回整个数组的长度,而不是指向数组的指针的长度。去一个数组名的地址所长生的是一个指向数组的指针,而不是一个指向某个指针常量的指针。
int a{10];
int b{10};
int *c;
c= & a{0};
表达式&a[0]是一个指向数组第一个元素的指针,但那正是数组名本身的值,所以同c=a;这里需要注意不是把a数组的内容拷贝到c中而仅仅是把a数组的指针geib。
b=a;
这条语句是非法的,你不能使用赋值符把一个数组的所有元素复制到另一个数组。必须使用一个循环每次复制一个元素
a=c;
c被声明为一个指针变量,这条语句看上去像是执行某种形式的指针赋值,把c的值复制给a但这个赋值是非法的,记住!在这个表达式中,a是个常量,不能被修改。
1.2下标引用
*(b+3) 表示的是a[3]所以 array [subscript] 和*( array + (subscript))等同的。
例子:
int arry[10];
int *ap= array +2;
记住,在进行指针加法运算时会对2进行调整。运算结果所产生的指针ap指向array[2.],以下是ap和array的对等表达式、
ap 这个看初始化表达式即可知道对等表达式为:array+2也就是&array{2}表示的是地址。
*ap 即为array{2}也可以表示为*(array +2)、
ap[0]一般以为ap不是数组不可以,但是 记住C的下标引用和间接访问表达式是一样的 。在这种情况下对等的表达式为*(ap + (0))除去0和括号结果与前面的表达式相同它的答案是array[2];
*ap+6 这里有连个操作符,间接访问先执行然后结果在于6相加。即为:array[2]+6、
*(ap +6)这个即为array[8]、
&ap这个表达式是合法的但是此时没有对等的array表达式,因为不能确定编译器会把ap放在先对array的什么位置。
ap[-1] 对等表达式为array[1].。
ap[9] 这个表达式看起来正确对等array[11]但是数组只有10个元素,这个表达式的结果是一个指针表达式,但是所指向的位置已经约过了右边界。根据标准这个是非法的,越界后你不确定会取到什么值,而编译器一般不提供这些检查,及时提供了会占用很多开销,所以可以自动开关。
2[array] 这个表达式 一般会以为非法的但是,它可以转换成对等的间接访问表达式,为*( 2+(array))最后即为*(array +2)。虽然这样做正确但是可读性降低。
1.3指针与下标
对于绝大多是人而言,下标更容易理解尤其是在多位数组中,但是指针表达式效率在某种场合更高。
1.4数组的指针
int a[5];
int *b;
声明一个数组时,编译器将根据声明所指定的元素数量维数组暴力内存空间,然后在创建数据名,它的值是一个常量,指向这段空间的起始位置。声明一个指针变量是,编译器只是为指针本身保留内存空间,它并不为仁和整型值分配内存空间。而且,指针变量斌给呗初始化为指向仁和现有的内存空间,如果他是一个自动变量,他甚至根本不会被初始化。
因此上述声明之后,表达式*a是完全合法的,但是表达式*b确实非法的。*b将访问内存空间中的不确定的位置,或者导致程序终止。另一方面,表达式b++可以通过编译,但是a++却不行以内a的值是个常量。
1.4初始化
例子:
int vector[5] = { 10, 20, 30 ,40, 50};
初始化列表给出的值诸葛赋值给数组的各个元素,所以vector[0]获得的值为10依次类推。
静态和自动初始化:数组初始化的方式类似于标量变量的初始化方式--也就是取决于它们的存储类型。存储于静态内存的数组只初始化一次,也就是在程序开始执行之前。程序并不需要执行指令把这些值放到合适的位置,他们一开始就在那里了。这个由连接器完成的,它用包含可执行的文件中合适的值对数组元素惊醒初始化。如果数组违背初始化,数组元素的初始值将会自动设置为零。当这些文件载入到内存中准备执行时,初始化后的数组值和程序指令也一样被载入到内存中。因此在程序执行之前
,静态数组已经初始化完毕。 但是,对于自动变量而言,因为自动变量位于运行时的对战中,执行流每次进入他们所在的代码块时,这类变量每次所处的内存可能并不相同。在程序开始之前,编译器没有办法对这些位置进行初始化。所以,自动变量在缺省情况是未初始化的。如果自动变量的声明中给出了初始值,每次当执行流进入自动变量声明所在的作用域时,变量就被一条隐式的赋值语句初始化。这条隐式的赋值语句和普通语句一样也需要时间和空间来执行。数组的问题在于初始化列表中可能有很多值,这就可能产生很多条赋值语句。对于大数组需要给予考虑。
当数组的初始化局部于一个函数时,数组每次进行重新初始化是不是值得的,如果不是则可以把数组声明为static型,这样数组的初始化只需要在程序开始前执行一次。
不完整的初始化:
int vector[5] = {1.2.3.4.5.6};
int vector[5] = {1.2.3.4};
在这种情况下,第一个声明时错误的,数组长度不够,但是第二个声明确实合法的,它的数组前面初始化正常后面不足的初始化为0. 注意:编译器只知道省略后面的前面会按照顺序进行初始化,而不会省略中间几个。
自动计算数组长度:
int vector [ ] = {1,2,3,4,5};
这时候编译器把数组长度设置为刚好能容纳所有的初始值得长度。
字符数组的初始化:
char message[]= { 'H' , 'e' , 'l' ,'l' , 'o', 0 };
同 char message [] = "Hello尽管看上去像字符串常量但是实际并不是只是前面初始化列表的另一种写法。
上面的区分例子
char message1 [] = ''Hello“;
char *message2 = "Hello";
两者的具体含义不同,前者初始化一个字符数组元素,后者则是真正的字符常量。这个指针变量初始化为只想这个字符敞亮的存储位置。
2.多维数组
我相信指针和数组之间的暧昧缠绵让很多C初学者很头痛吧,特别是多维数组,那真的是要了亲命,这里我给大家好好分析一下指针和多维数组之间的关系。
大家都知道一维数组名即是一个指针常量,它代表数组第一个元素的地址,我们知道一维数组的长度,那么可以通过数组名输出一维数组的所有元素:
#include <stdio.h>
int main(void)
{
int i;
int a[5] = {1, 2, 3, 4, 5};
int *p = a;
for( i = 0; i < 5; i++ )
printf( "%d\n", *(p + i) );
return 0;
}
但是在多维数组中,数组名却是第一个数组的地址,怎么理解呢?比如说定义一个二维数组:
int a[2][5] = {1, 2, 3, 4, 5,
6, 7, 8, 9, 10};
那么数组名a就是二维数组的第一行一维数组的地址,而不要错误的认为它代表的是第一行第一个元素的地址噢,那我们应该怎么正确的申明一个指向整形数组的指针呢?
int (*p)[5] = a;
它使 p 指向二维数组的第一行的一维数组(注意是指向的第一行一维数组)。
#include <stdio.h>
int main(void)
{
int i;
int a[2][5] = {1, 2, 3, 4, 5,
6, 7, 8, 9, 10};
int (*p)[5] = a;
for( i = 0; i < 5; i++ )
printf( "%d\n", *(*p + i) );
return 0;
}
上面的程序也是依次输出了1~5,这里 p 是指向二维数组的第一行地址的指针,那么 *p 即是代表第一行数组,那么也就是第一行数组的地址,我们可以再通过 *(*p + i) 输出第一行数组的各个元素。有点乱?呵呵,再仔细想想,或者直接说 a 是指向的第一行,那么 *a 就代表第一行的那个一维数组,但 *a 仍然是一个指针常量,而 **a 才是这个一维数组的第一个元素的值。
如果我们要定义一个指针可以逐个访问元素呢?下面两种方法都是正确的声明和赋值:
int *p = &a[0][0];
int *p = a[0];
第一种方法,就干脆把数组的第一行第一个元素取地址给指针变量p,这可能是最直观的方法。
第二种方法,其实原理一样,前面刚说了 a 是指向的第一行,第一行是一个一维数组,那么a[0]就是这个一维数组的第一个元素的地址。特别注意这里的 a[0] 不是一个值,而是一个地址。
#include <stdio.h>
int main(void)
{
int i;
int a[2][5] = {1, 2, 3, 4, 5,
6, 7, 8, 9, 10};
int *p = a[0];
for( i = 0; i < 10; i++ )
printf( "%d\n", *(p + i) );
return 0;
}
上面的代码就输出了二维数组中的所有元素。
附:这里的a[0]却输出了hello
int main(void)
{
char a[2][10] = {"hello", "hi"};
printf( "%s\n", a[0] );
//printf( "%s\n", *a );
//printf( "%s\n", *a
+ 1 ); 输出 "ello"
//printf( "%s\n", *(a
+ 1) ); 输出 "hi"
return 0;
}
而这又是为什么呢?这里的 a[0] 仍然是一个地址,它指向的是一个字符串常量,%s 是可以打印地址的,这里跟 %d 输出值是不一样的,a[0] 指向了字符串的第一个字母的地址,一直输出到 NUL 为止。
源博客多维数组和指针
可供参考博客多维数组
1.一维数组
1.1数组名
int a;
int b[10];
上面变量a称为标量,因为它是单一的值,这个变量的类型是一个整数。变量b成为数组,而不是标量因为踏实一些值得集合。下标和数组名仪器使用,用于标识该集合中某个特定的值。例如b{0}表示数组b的第一个值,而每个特定的值都是一个标量,可用于使用标量数据的上下文环境中。
b[0]的类型是整型,b表示什么呢?b是一个指针常量,是数组第一个元素的地址,它的类型取决于数组元素类型,这里即为int型。
注意这个值是指针常量,而不是指针变量。你不能改变常量的值。在程序完成链接之后,内存中的数组的位置是固定的,所以,数组名的值是一个之真敞亮。
两种场合下数组名并不是指针常量来表示。1.数组名作为sizeof操作符或者弹幕操作符&的操作数时。sizeof返回整个数组的长度,而不是指向数组的指针的长度。去一个数组名的地址所长生的是一个指向数组的指针,而不是一个指向某个指针常量的指针。
int a{10];
int b{10};
int *c;
c= & a{0};
表达式&a[0]是一个指向数组第一个元素的指针,但那正是数组名本身的值,所以同c=a;这里需要注意不是把a数组的内容拷贝到c中而仅仅是把a数组的指针geib。
b=a;
这条语句是非法的,你不能使用赋值符把一个数组的所有元素复制到另一个数组。必须使用一个循环每次复制一个元素
a=c;
c被声明为一个指针变量,这条语句看上去像是执行某种形式的指针赋值,把c的值复制给a但这个赋值是非法的,记住!在这个表达式中,a是个常量,不能被修改。
1.2下标引用
*(b+3) 表示的是a[3]所以 array [subscript] 和*( array + (subscript))等同的。
例子:
int arry[10];
int *ap= array +2;
记住,在进行指针加法运算时会对2进行调整。运算结果所产生的指针ap指向array[2.],以下是ap和array的对等表达式、
ap 这个看初始化表达式即可知道对等表达式为:array+2也就是&array{2}表示的是地址。
*ap 即为array{2}也可以表示为*(array +2)、
ap[0]一般以为ap不是数组不可以,但是 记住C的下标引用和间接访问表达式是一样的 。在这种情况下对等的表达式为*(ap + (0))除去0和括号结果与前面的表达式相同它的答案是array[2];
*ap+6 这里有连个操作符,间接访问先执行然后结果在于6相加。即为:array[2]+6、
*(ap +6)这个即为array[8]、
&ap这个表达式是合法的但是此时没有对等的array表达式,因为不能确定编译器会把ap放在先对array的什么位置。
ap[-1] 对等表达式为array[1].。
ap[9] 这个表达式看起来正确对等array[11]但是数组只有10个元素,这个表达式的结果是一个指针表达式,但是所指向的位置已经约过了右边界。根据标准这个是非法的,越界后你不确定会取到什么值,而编译器一般不提供这些检查,及时提供了会占用很多开销,所以可以自动开关。
2[array] 这个表达式 一般会以为非法的但是,它可以转换成对等的间接访问表达式,为*( 2+(array))最后即为*(array +2)。虽然这样做正确但是可读性降低。
1.3指针与下标
对于绝大多是人而言,下标更容易理解尤其是在多位数组中,但是指针表达式效率在某种场合更高。
1.4数组的指针
int a[5];
int *b;
声明一个数组时,编译器将根据声明所指定的元素数量维数组暴力内存空间,然后在创建数据名,它的值是一个常量,指向这段空间的起始位置。声明一个指针变量是,编译器只是为指针本身保留内存空间,它并不为仁和整型值分配内存空间。而且,指针变量斌给呗初始化为指向仁和现有的内存空间,如果他是一个自动变量,他甚至根本不会被初始化。
因此上述声明之后,表达式*a是完全合法的,但是表达式*b确实非法的。*b将访问内存空间中的不确定的位置,或者导致程序终止。另一方面,表达式b++可以通过编译,但是a++却不行以内a的值是个常量。
1.4初始化
例子:
int vector[5] = { 10, 20, 30 ,40, 50};
初始化列表给出的值诸葛赋值给数组的各个元素,所以vector[0]获得的值为10依次类推。
静态和自动初始化:数组初始化的方式类似于标量变量的初始化方式--也就是取决于它们的存储类型。存储于静态内存的数组只初始化一次,也就是在程序开始执行之前。程序并不需要执行指令把这些值放到合适的位置,他们一开始就在那里了。这个由连接器完成的,它用包含可执行的文件中合适的值对数组元素惊醒初始化。如果数组违背初始化,数组元素的初始值将会自动设置为零。当这些文件载入到内存中准备执行时,初始化后的数组值和程序指令也一样被载入到内存中。因此在程序执行之前
,静态数组已经初始化完毕。 但是,对于自动变量而言,因为自动变量位于运行时的对战中,执行流每次进入他们所在的代码块时,这类变量每次所处的内存可能并不相同。在程序开始之前,编译器没有办法对这些位置进行初始化。所以,自动变量在缺省情况是未初始化的。如果自动变量的声明中给出了初始值,每次当执行流进入自动变量声明所在的作用域时,变量就被一条隐式的赋值语句初始化。这条隐式的赋值语句和普通语句一样也需要时间和空间来执行。数组的问题在于初始化列表中可能有很多值,这就可能产生很多条赋值语句。对于大数组需要给予考虑。
当数组的初始化局部于一个函数时,数组每次进行重新初始化是不是值得的,如果不是则可以把数组声明为static型,这样数组的初始化只需要在程序开始前执行一次。
不完整的初始化:
int vector[5] = {1.2.3.4.5.6};
int vector[5] = {1.2.3.4};
在这种情况下,第一个声明时错误的,数组长度不够,但是第二个声明确实合法的,它的数组前面初始化正常后面不足的初始化为0. 注意:编译器只知道省略后面的前面会按照顺序进行初始化,而不会省略中间几个。
自动计算数组长度:
int vector [ ] = {1,2,3,4,5};
这时候编译器把数组长度设置为刚好能容纳所有的初始值得长度。
字符数组的初始化:
char message[]= { 'H' , 'e' , 'l' ,'l' , 'o', 0 };
同 char message [] = "Hello尽管看上去像字符串常量但是实际并不是只是前面初始化列表的另一种写法。
上面的区分例子
char message1 [] = ''Hello“;
char *message2 = "Hello";
两者的具体含义不同,前者初始化一个字符数组元素,后者则是真正的字符常量。这个指针变量初始化为只想这个字符敞亮的存储位置。
2.多维数组
我相信指针和数组之间的暧昧缠绵让很多C初学者很头痛吧,特别是多维数组,那真的是要了亲命,这里我给大家好好分析一下指针和多维数组之间的关系。
大家都知道一维数组名即是一个指针常量,它代表数组第一个元素的地址,我们知道一维数组的长度,那么可以通过数组名输出一维数组的所有元素:
#include <stdio.h>
int main(void)
{
int i;
int a[5] = {1, 2, 3, 4, 5};
int *p = a;
for( i = 0; i < 5; i++ )
printf( "%d\n", *(p + i) );
return 0;
}
但是在多维数组中,数组名却是第一个数组的地址,怎么理解呢?比如说定义一个二维数组:
int a[2][5] = {1, 2, 3, 4, 5,
6, 7, 8, 9, 10};
那么数组名a就是二维数组的第一行一维数组的地址,而不要错误的认为它代表的是第一行第一个元素的地址噢,那我们应该怎么正确的申明一个指向整形数组的指针呢?
int (*p)[5] = a;
它使 p 指向二维数组的第一行的一维数组(注意是指向的第一行一维数组)。
#include <stdio.h>
int main(void)
{
int i;
int a[2][5] = {1, 2, 3, 4, 5,
6, 7, 8, 9, 10};
int (*p)[5] = a;
for( i = 0; i < 5; i++ )
printf( "%d\n", *(*p + i) );
return 0;
}
上面的程序也是依次输出了1~5,这里 p 是指向二维数组的第一行地址的指针,那么 *p 即是代表第一行数组,那么也就是第一行数组的地址,我们可以再通过 *(*p + i) 输出第一行数组的各个元素。有点乱?呵呵,再仔细想想,或者直接说 a 是指向的第一行,那么 *a 就代表第一行的那个一维数组,但 *a 仍然是一个指针常量,而 **a 才是这个一维数组的第一个元素的值。
如果我们要定义一个指针可以逐个访问元素呢?下面两种方法都是正确的声明和赋值:
int *p = &a[0][0];
int *p = a[0];
第一种方法,就干脆把数组的第一行第一个元素取地址给指针变量p,这可能是最直观的方法。
第二种方法,其实原理一样,前面刚说了 a 是指向的第一行,第一行是一个一维数组,那么a[0]就是这个一维数组的第一个元素的地址。特别注意这里的 a[0] 不是一个值,而是一个地址。
#include <stdio.h>
int main(void)
{
int i;
int a[2][5] = {1, 2, 3, 4, 5,
6, 7, 8, 9, 10};
int *p = a[0];
for( i = 0; i < 10; i++ )
printf( "%d\n", *(p + i) );
return 0;
}
上面的代码就输出了二维数组中的所有元素。
附:这里的a[0]却输出了hello
int main(void)
{
char a[2][10] = {"hello", "hi"};
printf( "%s\n", a[0] );
//printf( "%s\n", *a );
//printf( "%s\n", *a
+ 1 ); 输出 "ello"
//printf( "%s\n", *(a
+ 1) ); 输出 "hi"
return 0;
}
而这又是为什么呢?这里的 a[0] 仍然是一个地址,它指向的是一个字符串常量,%s 是可以打印地址的,这里跟 %d 输出值是不一样的,a[0] 指向了字符串的第一个字母的地址,一直输出到 NUL 为止。
源博客多维数组和指针
可供参考博客多维数组
相关文章推荐
- C++ little errors , Big problem
- 回归基础之C语言程序2(getcwd() 和 chdir())
- c++ 程序设计 week3 作业
- Implementing a virtual machine in C(虚拟机C语言实现)
- 复习 C++ 中类的函数指针
- Sicily 2403. Voting
- STL编程题3(C++程序设计第9周)
- 字符串与数字转换-C语言
- STL编程题2(C++程序设计第9周)
- 初步剖析C语言编程中的结构体
- C、C++中关键字static的区别
- C/C++程序编译的内存分配情况
- 用C++设计一个不能被继承的类
- STL编程题1(C++程序设计第1周)
- Max Sum (HD_1003) 基础DP
- 【C语言】三种方法实现strlen函数
- 类的初步知识(C++中直角坐标和极坐标之间的转换代码)
- 用c语言指针实现给整形数组冒泡排序
- 一起talk C栗子吧(第一百零二回:C语言实例--使用信号量进行进程间同步与互斥三)
- 【C语言】字符串替换空格:请实现一个函数,把字符串中的每个空格替换成“%20”。