C语言其实不简单:数组与指针(共7篇)由网友“然后呢一起走吧”投稿提供,下面是小编整理过的C语言其实不简单:数组与指针,欢迎您阅读分享借鉴,希望对您有所帮助。
篇1:C语言其实不简单:数组与指针
之前在写C的时候,没怎么留意数组,就这么定义一个,然后颠来倒去的使用就行了,不过后来碰到了点问题,解决后决定写这么一篇博客,数组离不开指针,索性就放一起好了。
现在我定义了一个数组:int cc[10];
围绕这个数组有好几种指针:cc, cc+1, &cc[0], &cc, &cc+1等等。你知道它们都是什么含义吗?试试运行以下带代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include
int main
{
int cc[10];
printf(“%xn”, cc);
printf(“%xn”, cc+1);
printf(“%xn”, &cc[0]);
printf(“%xn”, &cc);
printf(“%xn”, &cc+1);
getchar();
return 0;
}
cc,这是学数组时第一个接触的“指针”,最为熟悉,它是数组的首个元素。cc+1,这是指向数字第二个位置的指针。
&cc[0],这个其实就是cc,指向数组的首个元素。
&cc,这是什么玩意儿?指向指针的指针?
&cc+1,如果上面的意思是指向指针的指针,那这个岂不是指向野地址了?
假设运行环境是32位机,并且数组首地址为0x28ff00,那么:
cc的结果为0x28ff00,这点毫无疑问。
cc+1的地址是0x28ff04而不是0x28ff01,因为一个int占用了4个字节的空间。cc+1其实是当成cc+1*sizeof(int)来看待。
&cc[0]的结果是0x28ff00,cc[0]表示的是数组的首个元素,那么&cc[0]自然就是首个元素的地址了。&cc[0] == cc。
&cc,这个就难说了,指针cc的值是0x28ff00,&cc表示这个指针本身的地址,我们怎么可能会知道这个地址?输出是个随机地址吗?随机数的话这个输出完全没有意义啊。如果不是随机地址的话,难不成还是0x28ff00?这样的话a不就等于&a了?明显不对吧,
。。
对于基本类型的指针,如int *tt; 那么*tt是其值,&tt是指针的地址,&tt != tt
但是上述的cc是个数组,实际上,&cc被编译成了&cc[0],但是其含义不同,&cc指向的是整个数组的开头。&cc与cc的指向可以用下图来形象表示:
上图可以看出,&cc其实代表的是int(*)[10],那么&cc+1就可以理解为cc + sizeof(cc)/4,之所以除以4是因为int型指针++其实是移动了4个字节。
又或者说%cc == cc + sizeof(cc)/4 == cc + 10,所以&cc+1的值为0x28ff28。
可见我们平常使用的数组名,并不能单纯的当成指针看待。数组名的本质是代表数组对象的变量名,是一个左值,是一个不能被改变的左值。但是由于在程序中不保存数组的大小,所以通过数组名只能访问数组的左值,不能访问数组的右值。由于这个原因,数组名在作为右值使用的时候被赋予另外一个新的意义——指向数组第一个元素的指针,这就是array-to-pointer转换规则。根据标准规定,只有当数组名作为sizeof、&运算符的操作数的时候,它是一个左值,其类型为数组类型。除此之外的所有情况,数组名都是一个右值,被编译器自动转换为指针类型,这种情况下我们就说数组名是一个指针,并且是一个指针常量。
接下来是另外一些有趣的东西,我们结合sizeof与数组输出各类值。以下程序的输出结果是什么?建议思考后再运行程序来验证答案。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include
int main()
{
int cc[10];
printf(“%dn”, sizeof(cc[0]));
printf(“%dn”, sizeof(cc));
printf(“%dn”, sizeof(&cc));
printf(“%dn”, sizeof(int(*)[10]));
getchar();
return 0;
}
sizeof(cc[0]),一个int的大小,输出4,没问题。sizeof(cc),注意不要和上面搞混,这不是数组首地址的指针,cc在这里是左值,其为数组类型,所以结果为40。
sizeof(&cc),这个的答案应该是多少呢?注意了,cc在这里还是左值,其为数组类型,但&cc不同于cc,不管数组怎么复杂它始终是个指针,32位机上指针大小始终是4个字节,所以结果为4。
篇2:(C语言)字符串比较函数,指针数组与数组指针
问题描述:
写一个函数,用于比较两个字符串的比较(string_compare).
程序分析:
(1)主要思想:传入两个字符串后,比较这两个字符串中的每个元素,如果第一次比较就不相等,就不要让它进入到下面的比较中,这样一来,将它返回一个相减的值(即:两数组中开始不相等的那两个元素相减,返回值(int类型),是ASCII码值相减)。进入比较的过程中时,相等就返回0;其他情况都返回那个相减的值。
(2)主要方式:定义指针数组,并对其初始化。然后照上面的思想,进行代码的实现。
代码如下:
/***指针数组(1)int *a[10] 是一个指针数组--->是一个数组(每个数组中的元素都是int*类型)(2)int (*a)[10] 是一个数组指针--->指向一个数组(十个int类型的数组) 注意:*,[],的优先级依次递增。下面使用了指针数组的例子,至于数组指针。。**/#include
篇3:深度理解C语言的指针与数组
写个简单的yuv读取的库,卡在多维数组动态分配的问题上,唉,还是C基本功不扎实,于是花了一下午时间,算是给自己有了点交代。参考《C专家编程》。水平有限,欢迎看客指正。
Section 1 左值与右值
编译器为每个变量分配一个地址(左值),该地址在编译时可知,且变量在运行时一直存于该地址。存于该地址的变量的值(右值)只有在运行时可知。因此,编译器如果需要一个地址来执行某种操作,它可以直接进行操作,如果需要一个变量的值,它需要发出指令从指定地址中读入变量值并存于寄存器中。到这里,可以理解作为一个指针变量,它本身的地址是左值,它变量的值(即指向的地址值)为右值。所以指针首先需要在运行时取得它的当前值,然后才能对它进行解除引用操作。
数组名是一个左值,即内存中的位置。但数组名是一个不可修改的左值,即不可被赋值。
int main
{
int a[3] = {0};
int b = 1;
a = &b; //ERROR: “=” : 左操作数必须为 l 值。
return 0;
}
Section 2 数组与指针的不同
一个例子:
int main()
{
char arr[4] = “abc”; // Note 1
//char arr[4] = {''a'', ''b'', ''c'', '' ''}; // Note 2
char *ptr = “ABC”; // Note 3
//ptr+1 = &arr[2]; // Note 4
printf(“arr: %x, %x, %x %x n”, &arr, &arr[0], &arr[1]); //Note 5
printf(“ptr: %x, %x, %x %x n”, &ptr, &ptr[0], &ptr[1]);
return 0;
}
Note 1&2等价定义,其结构如下:
a b c
[__] [__] [__] [__]
12fed4 +1 +2 +3
Note 3结构如下
42703c A B C
[__] [__] [__] [__] [__]
12fec8 42703c +1 +2 +3
Note 4复习一下Section 1.显然的错误,因为p+1首先需要知道p的值(右值),只有在运行时刻才能得到,编译时刻就希望对其所在的地址进行赋值显然错误,
Note 5验证Note1和3,运行结果如下:
arr: 12fed4, 12fed4, 12fed5
ptr: 12fec8, 42703c, 42703d
可以发现,arr的地址(左值)的结果与数组中首元素的地址一致,而ptr的变量值(右值)与数组的首元素地址一致。
因此对一个数组中的元素进行引用,c=arr[i]和c=ptr[i]都能够取出相应数组中的第i个元素。但要注意这两个操作的过程完全不同:
c = arr[i]; c = ptr[i];
1:取地址12fec8的内容,即42703c
1 取出i的值与12fed4相加 2:取出i的值与42703c相加
2 取地址(12fed4+ i)的内容 3:取地址(42703c+i)的内容
得到结论:尽管c=arr[i]和c=ptr[i]用同样的形式完成了同样的功能,但绝不可以混用。注意数组原始的声明方式,如果原始声明为数组式的,那么对其元素的引用要使用数组形式,反之亦然。
文件1中:
篇4:c语言指针
一、数组的指针、指针数组以及指向指针的指针
考虑数组的指针的时候我们要同时考虑类型和维数这两个属性,换一句话,就是说一个数组排除在其中存储的数值,那么可以用类型和维数来位置表示他的种类。
A)一维数组
在c和c++中数组的指针就是数组的起始地址(也就第一个元素的地址),而且标准文档规定数组名代表数组的地址(这是地址数值层面的数组表示)。例如:
int a[10]; int *p;
p=&a[0]//和p=a是等价的:
因为a是数组名,所以他是该数组的地址,同时因为第一个元素为a[0],那么&a[0]也代表了该数组的地址。但是我们是不是就说一个数组名和该数组的第一个元素的&运算是一回事呢?在一维的时候当时是的,但是在高维的时候,我们要考虑到维数给数组带来的影响。
a[10]是一个数组,a是数组名,它是一个包含10个int类型的数组类型,不是一般的指针变量噢!(虽然标准文档规定在c++中从int[]到int*直接转换是可以的,在使用的时候似乎在函数的参数为指针的时候,我们将该数组名赋值没有任何异样),a代表数组的首地址,在数字层面和a[10]的地址一样。这样我们就可以使用指针变量以及a来操作这个数组了。
所以我们要注意以下问题:
(1) p[i]和a[i]都是代表该数组的第i+1个元素;
(2) p+i和a+i代表了第i+1个元素的地址,所以我们也可以使用 *(p+I)和*(a+I)来引用对象元素;
(3)p+1不是对于指针数量上加一,而是表示从当前的位置跳过当前指针指向类型长度的空间,对于win32的int为4byte;
B)多维数组
对于二维数组a[4][6];由于数组名代表数组的起始地址,所以a(第一层)和第一个元素a[0][0]地址的数字是相同的,但是意义却是不同的。对于该数组我们可以理解为:a的一维数组(第一层),它有四个元素a[0]、a[1]、a[2]、a[3](第二层),而每个元素又含有6个元素a[0][0],a[0][1],a[0][2],a[0][3],a[0][4],a[0][5](第三层),…到此我们终于访问到了每个元素了,这个过程我们经历了:a->a[0]->a[0][0];
整体来讲:a是一个4行5列的二维数组,a表示它指向的数组的首地址(第一个元素地址&a[0]),同时a[0]指向一行,它是这个行的名字(和该行的第一个元素的首地址相同(第一个元素为地址&a[0][0]))。所以从数字角度说:a、a[0]、&a[0][0]是相同的,但是他们所处的层次是不同的。
既然a代表二维数组,那么a+i就表示它的第i+1个元素*(a+i)的地址,而在二维数组中
*(a+i)又指向一个数组,*(a+i)+j表示这个数组的第j+1个元素的地址,所以要访问这个元素可以使用 *(*(a+i)+j)(也就是a[i][j])。
他们的示意图为(虚线代表不是实际存在的):
对照这个图,如下的一些说法都是正确的(对于a[4][6]):
a是一个数组类型,*a指向一个数组;
a+i指向一个数组;
a、*a和&a[0][0]数值相同;
a[i]+j和*(a+i)+j是同一个概念;
总结一下就是:我们对于二维指针a,他指向数组a[0,1,2,3],使用*,可以使他降级到第二层次,这样*a就指向了第一个真正的数组。对于其他的情况我们也可以采用相同的方式,对于其他维数和类型的数组我们可以采用相类似的思想,
说到指向数组的指针,我们还可以声明一个指针变量让它指向一个数组。例如:
int (*p)[5];
这时p就是一个指针,要指向一个含有5个int类型元素的数组,指向其他的就会出现问题。
这个时候我们可以使用上面的什么东西来初始化呢?
我们可以使用*a,*(a+1),a[2]等。
原因很简单:我们在一个二维的数组中,那么表达方式有上面的相互类似的意义呢?只有 *a,*(a+1),a[2]等,
C)指针数组
一个指针数组是指一个数组中的每个元素都是一个指针,例如:
int *p[10];//而不能是int (*p)[10]
或者
char *p[10];
此时p是一个指针(数值上和&p[0]一样);
在前面有int t[10];
int * pt=t;//使用pt指向t
那么这里我们用什么指向int *t[10]中的t呢?我们要使用一个指针的指针:
int **pt=t;
这是因为:在int *t[10]中,每个元素是指针,那么同时t又指向这个数组,数组上和&t[0]相同,也就是指向t[0],指向一个指针变量,可以说是一个指针的指针了,所以自然要用
int **pt;
D)指针的指针
一个指针变量内部可以存储一个值,这个值是另外一个对象的地址,所以我们说一个指针变量可以指向一个普通变量,同样这个指针变量也有一个地址,也就是说有一个东西可以指向这个指针变量,然后再通过这个指针变量指向这个对象。那么如何来指向这个指针变量呢?由于指针变量本身已经是一个指针了(右值),那么我们这里就不能用一般的指针了,需要在指针上体现出来这些特点,我们需要定义指针的指针(二重指针)。
int *p1=&i; int**p2=&p1;
综合以上的所有点,下面是我们常常看到一些匹配(也是经常出错的地方):
int a[3],b[2][3],c,*d[3]; void fun1(int *p); void fun2(int (*p)[3]); void fun3(int **p); void fun4(int p[3]); void fun5(int p[]); void fun6(int p[2][3]); void fun7(int (&p)[3]);
函数 不会产生编译时刻的可能值(但逻辑上不一定都对)
函数
不会产生编译时刻的可能值(但逻辑上不一定都对)
fun1
a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i]
fun2
b,b+i,
fun3
d
fun4
a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i]
fun5
a, &a[i], *b ,b[i],&b[i][j] ,&c ,d[i]
fun6
b
篇5:C语言指针
指针变量是包含内存地址的变量,它指向内存中的一块区域,通过指针的值,可以间接访问到相应的内存单元的数据,并做相应的修改,
1、指针的定义和简单使用
定义一个指针变量和定义一般的变量类似,只需在变量名前面加一个“*”。对一个指针变量赋值可以用取地址符&来获取到一个变量的地址,如果要获得指针指向的内存区域的数据,用解参考运算符*(也称为间接运算符,它返回其操作数指向的对象的值)。指针的值为NULL(NULL是stdio.h中定义的符号变量,实际上是0)说明其不指向任何的内存单元,0是唯一直接可以赋值给指针变量的整数值。实际上,*和&是互补的,当两个运算符连续应用于一个指针变量时,无论顺序如何,运算结果相同。同时可以用printf中的格式化字符串%p来输出指针变量的值,下面是一个简单的程序。
[cpp]
#include
int main
{
int a;
a=9;
//定义并初始化一个指针,命名就可以看出
int *aPtr=NULL;
//将指针指向变量a
aPtr=&a;
printf(“The address of a is %p”
“nThe value of aPtr is %p”,&a,aPtr);
printf(“nnThe value of a is %d”
“nThe value of *aPtr is %d”,a,*aPtr);
printf(“nnShowing that * and & are complements of ”
“each othern&*aPtr = %p”
“n*&aPtr = %pn”,&*aPtr,*&aPtr);
return 0;
}
2、用指针做函数的参数
2.1 通过指针实现的引用传递
程序设计语言的参数传递方式,大致分两种:值传递和引用传递。C语言中没有引用传递,但是C语言通过指针间接实现了引用传递。通过用指针变量作为函数的参数,可以传递变量的地址(只需要在变量前面加上&运算符就可以),这样,用该地址就可以访问到主调函数中的该变量的内存地址,并可以进行相应的修改。这样,在函数执行完毕之后,修改仍然可以得到保留。
2.2 const
const限定符可以告诉编译器特定的变量的值是不能被修改的。如果想确保函数不会修改传递进来的参数值,应该将参数声明为const。这样对于C语言中用指针实现的引用传递,有四种情况:指向非常量数据的非常量指针(int *aPtr),指向非常量数据的常量指针(int *const aPtr),指向常量数据的非常量指针(const int *aPtr)和指向常量数据的常量指针(const int * const aPtr)。简单的说,就是指针变量自身和指针指向的变量都有可能是const,这样就产生了四种情况,这四种情况提供了四种不同的访问权限,下面分别解释。
指向非常量数据的非常量指针(int *aPtr):指针的值本身和指针指向变量的值都可以在函数中被修改。
指向非常量数据的常量指针(int *const aPtr):指针的值不能被修改,但是指针指向的变量的值可以被修改。
指向常量数据的非常量指针(const int *aPtr):指针指向的值不能被修改,但是指针本身的值可以被修改。
指向常量数据的常量指针(const int * const aPtr):指针本身和指针指向变量的值都不能被修改。
3、sizeof和指针运算
3.1 sizeof
sizeof是C语言中特殊的一元运算符,可以应用在变量名称、数据类型和常量之前,它在程序编译期间以字节为单位来确定数组或其他数据类型的大小。当应用于数组时,sizeof返回数组中的字节总数。如float a[20],sizeof(a)的值应该是4*20,80。当然,如果想获得数组的大小可以采用sizeof(a)/sizeof(float)。
3.2 指针运算
实际上,指针变量可以进行的算术操作是有限的:递增,递减,将指针和整数相加,从指针中减去一个整数或者一个指针减去另一个指针,
需要注意的是,对于指针的算术运算,其单位长度并不是一般意义上的1,而是sizeof(TYPE)。这样,如果float a[14]; float *aPtr=a;(或者int *aPtr=&a[0]); aPtr++;这样aPtr应该指向的是数组a的第二个元素,也就是a[1],这里的单位长度就是sizeof(float)。同样地,如果aPtr=aPtr+5;,这样aPtr又指向了数组a的第6个元素。如果aPtr-a,这样可以得到两个指针之间的元素间隔个数。应该是6。
进行指针运算要注意:
(1)如果将一个指针的值赋给另外一个指针,那么这两个指针的类型必须相同,否则应该用类型转换运算符进行类型转换。但是,有一个例外就是指向void类型的指针,它是通用指针,可以代表任何指针类型。因此,所有指针类型都可以赋值给void指针,而void指针也可以赋值给任何类型的指针,而不需要任何类型转换运算符。但是,void指针不能解参考,编译器知道指向int类型的指针引用的是32位计算机上的4个字节内存,但指向void的指针仅包含未知数据类型的内存位置,也就是说,编译器不知道指针所引用的字节数。编译器必须知道数据类型,才能确定所引用的字节数。
(2)除非两个指针变量都指向的是一个数组中的元素,否则对它们相减的结果没有任何意义,因为我们不能假设两个变量在内存中是连续的。
4、指针和数组
4.1 数组和指针的共性
实际上,数组名称的本质是一个常量指针。因此,int a[6]; int *aPtr;定义一个数组和指针之后,通过a[3],*(a+3)和*(aPtr+3)都可以访问到数组的第四个元素的值。但是区别在于,aPtr=aPtr+3;这样aPtr就指向了a数组的第四个元素,但是,不能a=a+3;,因为a是一个数组名,它是一个常量指针它的值不能被修改,更加具体地说,它应该是一个指向非常量数据的常量指针。
4.2 指针数组
数组元素也可以是指针类型,指针数组常见的用途就是构成由字符串组成的数组,简单地说就是字符串数组,数组中的每一个元素都是字符串。下面是一个例子,const限定符说明不能修改每个元素指针所指向的字符串。
[cpp]
#include
int main()
{
const char *suit[4]={“Hearts”,“Diamonds”,“Clubs”,“Spades”};
int i;
for(i=0;i<4;i++)
printf(“sizeof pointer to ”%s“ :%dn”,suit[i],sizeof(suit[i]));
printf(“nsizeof suit:%dn”,sizeof(suit));
return 0;
}
运行结果如下:
从这个例子中,可以看出const char *suit[4]={“Hearts”,“Diamonds”,“Clubs”,“Spades”};定义了一个指针数组,但是,其中的每个元素只是一个指针,而不是数组名(如果是数组名的话,sizeof的结果不应该都是4)。这样定义字符串数组可以节省空间。
5、函数指针
和数组名实际上就是数组第一个元素在内存中的地址类似,函数迷你实际上就是执行函数任务的代码在内存中的起始地址。函数指针包含函数在内存中的地址,可以传递给函数、从函数返回、存储在数组中或者是赋值给其它的函数指针,下面是两个函数指针的例子。
(1) 用函数指针实现升序/降序排序
[cpp]
#include
#define SIZE 10
int ascending(int a,int b)
{
return a>b;
}
int descending(int a,int b)
{
return a
}
void swap(int *aPtr,int *bPtr)
{
int temp=*aPtr;
*aPtr=*bPtr;
*bPtr=temp;
}
篇6:小小的C语言问题指针数组赋值关于指针和数组。
先上代码吧:
?
#include
#include
using namespace std ;
int replacefun(char* str, char c1, char c2);
int main(void)
{
char * p = “I love you China, do you love me?”; // 用指针的形式定义一个字符数组
int m = 0;
m = replacefun(p, 'o', 'c');
cout << m << endl;
return 0;
}
int replacefun(char* str, char c1, char c2)
{
int num = 0;
while (*str != ' ')
{
if (*str == c1)
{
*str = c2; // 这句话调试的时候内存报错
num++;
}
str++;
}
return num;
}
这个程序实现的目的的是替换指定的字符,在编译的时候没有任何错误,但是在运行的时候,程序意外终止,
小小的C语言问题指针数组赋值关于指针和数组,
电脑资料
《小小的C语言问题指针数组赋值关于指针和数组。》()。。
于是,启动利器,单步调试。。。跳进函数后,发现在*str到达'o'之前一切正常,不过。。到了'o'之后,结果说内存错误,无法赋值。
经过多方查找资料和询问,得知:
char *p=“abcde”;的时候,字符串是不可以修改的!
而char p[]=“abcde”;的时候,字符串是可以修改的!
同一个字符串,用指针定义的时候在常量区,而用数组定义的时候在栈中。
就是刚刚上面的那个
I love you China, do you love me?
是一个常量。
据大婶说:
“I love you China, do you love me?”
在存放在.rodata段,该段是只读的,当你强行做修改的时候,当然要报错喽。
而定义为数组的形式之后:就把这个字符串拷贝进数组了,对于数组中的内容,可以随便修改呀. 原来的字符串还是不变的。。。
以上总结,涨姿势了。
继续C++。
篇7:c中指针指针、指针的指针、指针数组和数组指针
一、指针
如果在程序中定义一个变量,在对程序进行编译,系统会自动给这个变量分配内存单元,根据不同的类型,分配不同长度的空间,如int占用4个字节,char占用1个字节,内存单元中每个字节都有编号,这就是地址。由于可通过地址能够找到所需的变量单元,可以说地址指向该变量单元。打个比方,一个房间的门口挂了一个房间号301,这个301就是房间的地址,将该地址形象化为指针。对于一个内存单元来说,单元的地址(编号)即为指针,其中存放的数据才是该单元的内容。
严格地说,一个指针是一个地址,是一个常量,
而一个指针变量却可以被赋予不同的指针值,是变量。但常把指针变量简称为指针。为了避免混淆,约定:“指针”是指地址,是常量,“指针变量”是指取值为地址的变量。定义指针的目的是为了通过指针去访问内存单元。
例如:
int a=12;
int *p=&a;
二、指针的指针(二级指针)
简单来说,二级指针变量就是一级指针变量的地址。
例如:
int a=12;
int *p=&a;
int **=&p;
★ c语言学习心得
【C语言其实不简单:数组与指针(共7篇)】相关文章:
C和指针 (pointers on C)――第十二章:使用结构和指针2022-05-23
C++多重继承的指针问题2023-06-13
C笔试题及答案2024-01-12
c语言笔试题目及答案2023-06-22
c语言学习总结2023-11-26
安全员c考试试题及答案2022-04-29
高校非计算机专业《C程序设计》无纸化考试模式探讨2023-09-18
学习计算机技巧2023-07-22
9月计算机二级考试C语言预测试题2022-04-30
C语言嵌套注释2022-09-23