c和指针

本文深入浅出地讲解了C语言中的指针概念,包括指针的类型、指向的类型、值以及自身占据的内存区等内容,并通过实例介绍了指针的算术运算、数组与指针的关系等关键知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 
初学者服是我的帖子的宗旨。我也是个初学者(强调了无数遍了),我以我的理解把初学者懂的西用浅言写出来。由于小学时语文没学好,所以竭尽全力也未必能达到个目的。尽力而吧。
cc++中的点和重点。我只精通dos下的basicc言的其它各特性,basic中都有似的西。只有指,baisc所不具的。指c的灵魂。
我不想重大多数得很清楚的西,我只是把我看得不清楚或没有,而我又得我理解得有点道理的西写出来。我的目的是: 西,把我袋中c的模糊的知清晰化。
第一章。指的概念
是一个特殊的,它里面存的数被解内存里的一个地址。要搞清一个指需要搞清指的四方面的内容:,所指向的,或者叫指所指向的内存区,有指本身所占据的内存区。别说明。
先声明几个指放着做例子:
例一:
(1)int *ptr;
  (2)char *ptr;  (3)int **ptr;   (4)int (*ptr)[3];   (5)int *(*ptr)[4];
1型。
法的角度看,你只要把指声明句里的指名字去掉,剩下的部分就是个指型。是指本身所具有的型。看看例一中各个指
(1)int *ptr; //型是int *      (2)char *ptr; //型是char *   
(3)int **ptr; //型是 int **   (4)int (*ptr)[3]; // int(*)[3]
(5)int *(*ptr)[4]; //
型是 int *(*)[4]
么样?找出指型的方法是不是很简单?
2。指所指向的型。
当你通访问所指向的内存区,所指向的型决定了编译器将把那片内存区里的内容当做什来看待。法上看,你只把指声明句中的指名字和名字左的指声明符*去掉, 剩下的就是指所指向的型。例如:
(1)int *ptr; //所指向的型是int
(2)char *ptr; //
所指向的的char
(3)int **ptr; //
所指向的的型是 int *
(4)int (*ptr)[3]; //
所指向的的型是 int()[3]
(5)int *(*ptr)[4]; //
所指向的的型是 int *()[4]
在指的算运算中,所指向的型有很大的作用。(即指本身的)和指所指向的型是两个概念。当你C越来越熟悉,你会发现,把与指针搅和在一起的""个概念分成"""所指向的"两个概念,是精通指关键点之一。我看了不少,发现有些写得差的,就把指两个概念在一起了,所以看起来前后矛盾,越看越糊涂。
3,或者叫指所指向的内存区或地址。
是指本身存的数,将被编译器当作一个地址,而不是一个一般的数。在32位程序里,所有型的指都是一个32位整数,32位程序里内存地址全都是32
所指向的内存区就是从指所代表的那个内存地址,sizeof(所指向的)的一片内存区。以后,们说一个指XX,就相当于说该指向了以XX首地址的一片内存区域;我们说一个指指向了某内存区域,就相当于说该这块内存区域的首地址。
所指向的内存区和指所指向的型是两个完全不同的概念。在例一中,所指向的型已有了,但由于指针还未初始化,所以它所指向的内存区是不存在的,或者是无意的。
以后,遇到一个指,应该问问:个指型是什?指向的型是什?指向了哪里?
4本身所占据的内存区。
本身占了多大的内存?你只要用函数sizeof()一下就知道了。在32位平台里,本身占据了4个字度。本身占据的内存个概念在判断一个指表达式是否是左值时很有用。
第二章。指的算运算
可以加上或减去一个整数。指这种运算的意和通常的数的加减运算的意是不一的。例如:
例二:
1
char a[20];   2 int *ptr=a;   3 ptr++;
在上例中,ptrint*,它指向的型是int,它被初始化指向整形a接下来的第3句中,ptr被加了1,编译器是这样处理的:它把指ptr加上了sizeof(int),32位程序中,是被加上了4由于地址是用字位的,ptr所指向的地址由原来的a的地址向高地址方向增加了4个字
由于char型的度是一个字,所以,原来ptr是指向数a的第0始的四
个字,指向了数a中从第4始的四个字
可以用一个指和一个循来遍一个数,看例子:
例三:
int array[20];
  int *ptr=array; //略去整型数组赋值的代
for(i=0;i<20;i++)
{
(*ptr)++;
ptr++

}
 个例子将整型数中各个元的1。由于次循都将指ptr1,所以次循都能访问的下一个元。再看例子:
例四:
1
char a[20];   2 int *ptr=a;   3 ptr+=5;
个例子中,ptr被加上了5,编译器是这样处理的:将指ptr加上5sizeof(int),32位程序中就是加上了54=20。由于地址的位是字,在的ptr所指向的地址比起加5后的ptr所指向的地址来,向高地址方向移20个字。在个例子中,没加5前的ptr指向数a的第0始的四个字,5,ptr指向了数a的合法范之外了。这种情况在用上会出问题,但在法上却是可以的。也体出了指的灵活性。
如果上例中,ptr是被减去5,么处程大同小异,只不ptr是被减去5sizeof(int),新的ptr指向的地址将比原来的ptr所指向的地址向低地址方向移20个字
总结一下,一个指ptrold加上一个整数n,果是一个新的指ptrnew,ptrnew型和ptrold型相同,ptrnew所指向的型和ptrold所指向的型也相同。ptrnew将比ptrold增加了nsizeof(ptrold所指向的)个字就是,ptrnew所指向的内存区将比ptrold所指向的内存区向高地址方向移nsizeof(ptrold所指向的)个字
一个指ptrold减去一个整数n,果是一个新的指ptrnew,ptrnew型和ptrold型相同,ptrnew所指向的型和ptrold所指向的型也相同。ptrnew将比ptrold减少了nsizeof(ptrold所指向的)个字,就是,ptrnew所指向的内存区将比ptrold所指向的内存区向低地址方向移nsizeof(ptrold所指向的)个字
第三章。运算符&*
&是取地址运算符,*...上叫做"接运算符"&a的运算果是一个指,型是a型加个*,所指向的型是a,所指向的地址嘛,那就是a的地址。
*p的运算果就五花八了。*p果是p所指向的西,西有些特点:它的型是p指向的,它所占用的地址是p所指向的地址。
例五:
int a=12;
  int b;  int *p;  int **ptr;
p=&//&a
果是一个指,型是int*,指向的型是int,指向的地址是a的地址。
*p=24;//*p,里它的型是int,它所占用的地址是p所指向的地址,,*p就是a
ptr=&//&p
果是个指,型是p型加个*,里是int**所指向的型是p,里是int*所指向的地址就是指p自己的地址。
*ptr=&b//*ptr
是个指,&b果也是个指,两个指型和所指向的型是一,所以用&b*ptr赋值就是毫无问题的了。
**ptr=34;//*ptr
果是ptr所指向的西,里是一个指,对这个指再做一次*运算,果就是一个int型的量。
第四章。指表达式。
一个表达式的最后果如果是一个指,么这个表达式就叫指表达式。
下面是一些指表达式的例子:
例六:
int a,b;
int array[10];
int *pa;
pa=&//&a
是一个指表达式。
int **ptr=&//&pa
也是一个指表达式。
*ptr=&//*ptr
&b都是指表达式。
pa=array;
pa++;//
也是指表达式。
例七:
char *arr[20];
char **parr=arr;//
如果把arr看作指,arr也是指表达式
char *str; 
str=*parr;//*parr
是指表达式
str=*(parr+1);//*(parr+1)
是指表达式
str=*(parr+2);//*(parr+2)
是指表达式
由于指表达式的果是一个指,所以指表达式也具有指所具有的四个要素:,所指向的,指向的内存区,自身占据的内存。
好了,当一个指表达式的果指明确地具有了指自身占据的内存的,个指表达式就是一个左,就不是一个左
在例七中,&a不是一个左,没有占据明确的内存。*ptr是一个左,*ptr个指占据了内存,*ptr就是指pa,既然pa在内存中有了自己的位置,*ptr当然也有了自己的位置。
第五章。数和指
如果声明数句不太明白的,我前段时间贴出的文章<<如何理解cc++复杂类型声明>>
的数名其可以看作一个指?聪吕?
例八:
int array[10]={0,1,2,3,4,5,6,7,8,9},value;
value=array[0];//
也可写成:value=*array;
value=array[3];//
也可写成:value=*(array+3);
value=array[4];//
也可写成:value=*(array+4);
上例中,一般而言数array代表数本身,型是int [10],但如果把array看做指,它指向数的第0,型是int *,所指向的型是数组单元的型即int。因此*array等于0就一点也不奇怪了。同理,array+3是一个指向数3元的指,所以*(array+3)等于3。其它依此推。
例九:
char *str[3]={ "Hello,this is a sample!","Hi,good morning.","Hello world" };
char s[80]

strcpy(s,str[0]);//
也可写成strcpy(s,*str);
strcpy(s,str[1]);//
也可写成strcpy(s,*(str+1));
strcpy(s,str[2]);//
也可写成strcpy(s,*(str+2));
上例中,str是一个三元的数,元都是一个指,些指各指向一个字符串。把指str当作一个指,它指向数的第0,它的型是char**,它指向的型是char *
*str也是一个指,它的型是char*,它所指向的型是char,它指向的地址是字符串"Hello,this is a sample!"的第一个字符的地址,'H'的地址。
str+1
也是一个指,它指向数的第1,它的型是char**,它指向的型是char *
*(str+1)也是一个指,它的型是char*,它所指向的型是char,它指向"Hi,good morning."的第一个字符'H',等等。
下面总结一下数的数名的问题。声明了一个数TYPE array[n],名称array就有了两重含:第一,它代表整个数,它的型是TYPE [n];第二,它是一个指,型是TYPE*,指向的型是TYPE,也就是数组单元的,指向的内存区就是数0,自己占有独的内存区,注意它和数0元占据的内存区是不同的。是不能修改的,array++的表达式是错误的。
在不同的表达式中数array可以扮演不同的角色。
在表达式sizeof(array),array代表数本身,这时sizeof函数出的是整个数的大小。
在表达式*array,array扮演的是指,因此个表达式的果就是数0元的sizeof(*array)出的是数组单元的大小。
表达式array+n(其中n=0,1,2,....),array扮演的是指,array+n果是一个指,它的型是TYPE*,它指向的型是TYPE,它指向数n元。故sizeof(array+n)出的是指针类型的大小。
例十:
int array[10];
int (*ptr)[10];
ptr=&
上例中ptr是一个指,它的型是int (*)[10],他指向的型是int [10],用整个数的首地址来初始化它。在ptr=&array,array代表数本身。
中提到了函数sizeof(),我来,sizeof(名称)出的究竟是指自身型的大小呢是指所指向的型的大小?答案是前者。例如:
int (*ptr)[10];
32位程序中,:
sizeof(int(*)[10])==4
sizeof(int [10])==40
sizeof(ptr)==4
实际,sizeof()出的都是象自身的型的大小,而不是的什么类型的大小。
第六章。指型的
可以声明一个指向象的指
例十一:
struct MyStruct
{ int a;
int b;
int c;
}
MyStruct ss={20,30,40};//
声明了ss,并把ss的三个成初始化20,3040
MyStruct *ptr=&//
声明了一个指向ss的指它的型是MyStruct*,它指向的型是MyStruct
int *pstr=(int*)&//声明了一个指向ss的指。但是它的型和它指向的型和ptr是不同的。请问ptr访问ss的三个成员变?
答案: ptr->a; ptr->b; ptr->c;
请问pstr访问ss的三个成员变?
答案: *pstr//访问ss的成a *(pstr+1);//访问ss的成b *(pstr+2)//访问ss的成c呵呵,然我在我的MSVC++6.0上述代,但是要知道,这样使用pstr访问结构成是不正,不正,看看怎访问的各个:
例十二:
int array[3]={35,56,37};
int *pa=array;
pa访问array的三个元的方法是:
*pa;//
访问了第0
*(pa+1);//
访问了第1
*(pa+2);//
访问了第2
从格式上看倒是与通针访问结构成的不正方法的格式一
所有的C/C++编译器在排列数,是把各个数组单元存放在连续的存区里,元和元之没有空隙。但在存放象的各个成员时,在某种编译环境下,可能会需要字对齐或双字对齐或者是的什么对齐,需要在相两个成加若干"填充字",致各个成可能会有若干个字的空隙。
所以,在例十二中,即使*pstr访问到了ss的第一个成员变a,也不能保*(pstr+1)就一定能访问构成b。因a和成b可能会有若干填充字,不定*(pstr+1)就正好访问到了些填充字呢。明了指的灵活性。要是你的目的就是想看看各个构成到底有没有填充字,,倒是个不的方法。
针访问结构成的正确方法应该是象例十二中使用指ptr的方法。 
第七章。指和函数的
可以把一个指声明成一个指向函数的指
int fun1(char*,int);
int (*pfun1)(char*,int);
pfun1=fun1;
int a=(*pfun1)("abcdefg",7);//
函数指饔煤? 
可以把指函数的形参。在函数句中,可以用指表达式来作参。
例十三:
int fun(char*);
int a;
char str[]="abcdefghijklmn";
a=fun(str);
int fun(char*s)
{ int num=0;
for(int i=0;i
{  num+=*s;s++; }
return num;
)
个例子中的函数fun统计一个字符串中各个字符的ASCII码值之和。前面,的名字也是一个指。在函数用中,当把str为实传递给形参s,实际是把str值传递给s,s所指向的地址就和str所指向的地址一致,但是strs各自占用各自的存。在函数体内s行自加1运算,并不意味着同时对str行了自加1运算。
第八章。指针类转换
当我初始化一个指一个指针赋值时,赋值号的左是一个指,赋值号的右是一个指表达式。在我前面所的例子中,大多数情况下,型和指表达式的型是一,所指向的型和指表达式所指向的型是一的。
例十四:
1
float f=12.3;
2
float *fptr=& 
3
int *p;
在上面的例子中,假如我p指向f,应该?是用下面的?
p=&
。因p型是int*,它指向的型是int。表达式&f果是一个指,型是float*,它指向的型是float。两者不一致,直接赋值的方法是不行的。至少在我的MSVC++6.0,赋值语句要求赋值号两型一致,所指向的型也一致,其它的编译器上我没试过,大家可以试试实现的目的,需要"转换":
p=(int*)&
如果有一个指p,需要把它的型和所指向的型改TYEP*TYPE,么语法格式是: (TYPE*)p
这样强转换果是一个新指,新指型是TYPE*,它指向的型是TYPE,它指向的地址就是原指指向的地址。而原来的指p的一切属性都没有被修改。
一个函数如果使用了指形参,在函数句的参和形参的程中,也会生指针类型的转换
例十五:
void fun(char*);
int a=125,b;
fun((char*)&a);
void fun(char*s)
{ char c;
c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;
c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;
}
}
注意是一个32位程序,int型占了四个字,char型占一个字。函数fun的作用是把一个整数的四个字序来个倒。注意到了?在函数句中,&a果是一个指,它的型是int *,它指向的型是int。形参个指型是char*,它指向的型是char这样,参和形参的程中,须进行一次从int*型到char*型的转换个例子,可以这样来想象编译转换:编译器先构造一个临时 char*temp,然后temp=(char*)&a,最后再把temp值传递给s。所以最后的果是:s型是char*,它指向的型是char,它指向的地址就是a的首地址。
知道,就是指指向的地址,32位程序中,是一个32位整数。那可不可以把一个整数当作指直接赋给?就象下面的:
unsigned int a;
TYPE *ptr;//TYPE
int,char型等等型。
a=20345686;
ptr=20345686;//
的目的是要使指ptr指向地址20345686()
ptr=a;//
的目的是要使指ptr指向地址20345686()
编译一下吧。发现后面两条句全是的。那的目的就不能达到了?,:
unsigned int a;
TYPE *ptr;//TYPE
int,char型等等型。
a=
某个数,个数必代表一个合法的地址;
ptr=(TYPE*)a
//呵呵,就可以了。
里的(TYPE*)和指针类转换中的(TYPE*)不一里的(TYPE*)的意思是把无符号整数a当作一个地址来看待。
上面强调a代表一个合法的地址,,在你使用ptr,就会出非法操作错误
想想能不能反,把指指向的地址即指当作一个整数取出来。完全可以。下面的例子演示了把一个指当作一个整数取出来,然后再把个整数当作一个地址赋给一个指:
例十六:
int a=123,b;
int *ptr=&
char *str;
b=(int)ptr;//
把指ptr当作一个整数取出来。
str=(char*)b;//
个整数的当作一个地址赋给str
好了,在我知道了,可以把指当作一个整数取出来,也可以把一个整数当作地址赋给一个指
第九章。指的安全问题
看下面的例子:
例十七:
char s='a';
int *ptr;
ptr=(int*)&
*ptr=1298
ptr是一个int*型的指,它指向的型是int它指向的地址就是s的首地址。32位程序中,s占一个字,int型占四个字。最后一句不但改s所占的一个字,把和s的高地址方向的三个字也改了。三个字是干什?只有编译程序知道,而写程序的人是不太可能知道的。也许这三个字里存了非常重要的数据,许这三个字里正好是程序的一条代,而由于你,三个字被改,会造成崩性的错误
再来看一例: 例十八:
1
char a;
2
int *ptr=&
3
ptr++;
4
*ptr=115;
例子完全可以通过编译,并能行。但是看到没有?3ptr行自加1运算后,ptr指向了和整形a的高地址方向的一区。这块区里是什? 不知道。有可能它是一个非常重要的数据,甚至可能是一条代。而第4句竟然往片存区里写入一个数据,重的错误。所以在使用指针时,程序心里必非常清楚:我的指究竟指向了哪里。在用指针访问,也要注意不要超出数的低端和高端界限,也会造成似的错误在指转换:ptr1=(TYPE*)ptr2,如果sizeof(ptr2)大于sizeof(ptr1),在使用指ptr1访问ptr2所指向的存是安全的。如果sizeof(ptr2)小于sizeof(ptr1),在使用指ptr1访问ptr2所指向的存是不安全的。至于,合例十七来想一想,应该会明白的
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值