[学习笔记]C语言中关于指针的详解1

Posted 正当梨花开满天涯

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[学习笔记]C语言中关于指针的详解1相关的知识,希望对你有一定的参考价值。

[学习笔记]C语言中关于指针的详解 1

什么是指针

操作系统给每个存储单元分(即一个字节)配了一个编号,从0x00 00 00 00 到 0xff ff ff ff
这个编号称之为地址
指针就是这个地址
c语言中 占多个多字节的变量 存储单元编号最小的那个编号 是多字节变量的地址编号

指针变量

指针变量是一个只用来存储地址的变量 但不可随意赋值一个0x00 00 00 01之类的值
因为这个地址可能还没有开辟内存空间 指针变量只能保存开辟好空间的地址
在32位的系统下 地址总线是32位的 所以地址编号是32位的
所以指针变量是32位的 即4个字节

指针的类型

无论什么类型的指针变量 存储的都是地址 且都是4个字节(32位系统)
对应类型的指针变量 只能存放对应类型的变量的地址
1.字符指针 char *p
2.短整形指针 short *p
3.整形指针 int p
4.长整形指针 long
p
5.浮点型指针 float *p
6.double型指针 double *p
7.函数指针
8.结构体指针
9.数组指针
10.指针的指针

指针的定义

数据类型 * 变量名;
例: int *p;
定义变量的时候 *用来修饰变量 代表p是一个指针变量

指针的运算符

在定义时 * 标识这是一个指针变量  在其他时候表示取值
&  表示取一个变量的地址
例: 
int a = 10int *p; 
定义一个整形指针变量 p 和一个 整形变量a 值为10
p = &a;    
将a的地址给p
cout<<*p;
此时输出p的值 为10
		

数组的指针

c语言规定 数字的名字就是数组的首地址 即第0个元素的地址,是个常量
    int a[10];
    a[0]=1;
    a[1]=2;
    int *p=a;
    printf("数组地址:%p 值地址:%p 值%d\\n",a,&a[1],a[1]);
    printf("数组地址:%p 值地址:%p 值%d\\n",p,&p[1],p[1]);
输出结果:
	数组地址:0x7ff7bce31940 值地址:0x7ff7bce319442
	数组地址:0x7ff7bce31940 值地址:0x7ff7bce319442

注意 p和a不同, p是指针变量 而a是一个常量 是数组的首地址
例如 指针变量p可以进行 p++ 自增运算 但数组名a就不可以
a是数组的名字,是a[0]的地址,p=a即p也保存了a[0]的地址,即a和p都指向a[0] 所以在引用数组元素的时候a和p等价
不同的是 a是一个常量 存储的是a[0]的地址 而p是一个变量 所以a不可以用=赋值 p可以赋值
对a取地址和对p取地址 结果不同 对a取地址 结果为数组指针 而对p取地址 结果是指针的指针

指针的运算

指针可以加一个整数,结果还是个地址
    int a[10] = 1,2,3,4,5;
    int *p=&a[1];   //将数组a中第1个元素的地址给p   此时p的值为2
    printf("数组地址:%p 值地址:%p 值%d\\n",p,&p[0],*p);
    p++;     //将指针自增1 指向下一个地址   即a[2]
    printf("数组地址:%p 值地址:%p 值%d\\n",p,&p[0],*p);
输出结果:
	数组地址:0x7ff7bbd0b944 值地址:0x7ff7bbd0b9442
	数组地址:0x7ff7bbd0b948 值地址:0x7ff7bbd0b9483
	
   字符数组也可以这样操作
    char a[10]= "hello";
    char *p = a;
    printf("数组地址:%p 值地址:%p 值: %c\\n",p,&p[0],*p);
    p += 2;    //指向后面第二个地址 即a[2]
    printf("数组地址:%p 值地址:%p 值: %c\\n",p,&p[0],*p);
输出结果:
	数组地址:0x7ff7bc3e095e 值地址:0x7ff7bc3e095e: h
	数组地址:0x7ff7bc3e0960 值地址:0x7ff7bc3e0960: l

指针之前可以比较大小 前提是两个相同类型的指针指向同一个数组的元素的时候,比较大小才有意义。
指向前面元素的指针 小于 指向后面元素的指针
两个相同类型的指针可以相互赋值 (void *类型的除外)

指针数组

一个数组中所有的元素都是类型相同的指针变量,这个数组就是指针数组
指针数组的定义:
	int *p[10];     //定义一个有10个元素的数组指针 每个元素都是int类型的指针变量
	和普通指针变量类似 数组指针也有不同类型 例如float double long 结构体等指针数组
	一般情况下 指针数组用来保存多个字符串
	    char *p[3] = "How","dare","you";
	    int i = 3;
	    for(i=0;i<3;i++)
	        printf("%s ",p[i]);
	    
	输出结果:
		How dare you 

指针的指针

指针的指针 即指针的地址
一个指针变量本身占4个字节 指针变量也有地址编号
int a;  //定义一个变量a
int *p = &a; //定义一个指针变量p 将a的地址给p
那么此时 *p就等于a
int **p2 = &p; // 定义一个指针变量p2(二级指针) 将p的地址给p2
那么此时 *p2就等于p  **p2就等于a
int ***p3 = &p2 定义一个指针变量p3(三级指针)同理 ***p3等价于a
p p2 p3都是指针变量 都占4个字节
上代码帮助理解:
    int a = 10;
    int *p = &a;
    int **p2 = &p;
    int ***p3 = &p2;
    printf("a=%d\\n*p=%d\\n**p2=%d\\n***p3=%d\\n",a,*p,**p2,***p3);
    printf("&a=%p\\n&p=%p\\n&p2=%p\\n&p3=%p\\n",&a,p,p2,p3);
    printf("&a=%p\\n&p=%p\\n&*p2=%p\\n&**p3=%p\\n",&a,p,*p2,**p3);
输出结果:
	a=10
	*p=10
	**p2=10
	***p3=10
	&a=0x7ff7bc836968
	&p=0x7ff7bc836968
	&p2=0x7ff7bc836960
	&p3=0x7ff7bc836958
	&a=0x7ff7bc836968
	&p=0x7ff7bc836968
	&*p2=0x7ff7bc836968
	&**p3=0x7ff7bc836968

字符串和指针

字符串的概念: 字符串就是以'\\0'为结尾的若干字符的集合
字符串储存的形式有:数组、字符串指针、堆
字符串数组: 
	char str[10] = "hello";
	定义一个字符数组 str  字符串hello存放在变量str中 可按下标取值
字符串指针:
	char *str = "hello";
	定义一个字符串指针变量 str  字符串指针变量只能存放字符地址编号
	即 hello这个字符串不存放在str中  str只存放了 h 的地址编号 hello存放在文字常量区
堆:
	char *str = (char*)malloc(10*sizeof(char));
	动态申请了10个字节的空间,首地址给str赋值

1.栈和全局内存中的内容是可以修改的 例如字符串数组 str[0]=“p”; 可以修改
2.文字常量区的内容是不可修改的 字符串指针 *str = “haha”;则不可以修改
3.堆区的内容是可以修改的 堆 *str =“haha”; 则可以修改
字符数组,指针定义的str可以在定义时直接初始化赋值,堆中存放的字符串不能初始化,只能通过strcpy,scanf赋值

数组指针

数组指针的概念: 本身是一个指针,指向一个数组,+1跳一个数组,即指向下一个数组
数组指针的作用就是可以保存二维数组的首地址
数组指针的定方法:指向的数组的类型+*指针变量名)[指向的数组的元素个数]
	int(*p)[5];   //定义了一个数组指针变量p  p指向的是有5个整型元素的数组
数组指针对于二维数组的应用:
	int a[3][5];  //定义一个3行5列的二维数组
    int (*p)[5];  //定义一个数组指针 p+1会指向下一个有5个整型元素的数组
    printf("a[0]的地址: %p  a[1]的地址: %p\\n",&a[0],&a[1]);
    p=a;
    printf("p的地址: %p  p+1的地址: %p",p,p+1);   //p+1指向下一个元素 即a[1]
输出结果:
	a[0]的地址: 0x7ff7b1cfd920  a[1]的地址: 0x7ff7b1cfd934
	p的地址: 0x7ff7b1cfd920  p+1的地址: 0x7ff7b1cfd934
数组指针可以将二维数组的首地址传递到另一个函数中,此时函数的形参就要定义为数组指针
一维数组指针就是二维数组的首地址 二维数组指针就是三维数组的首地址 以此类推
多维数组的数组名取*代表指针降级

指针与函数的关系

函数的形参有int long char float等,除此之外也可以给函数传递一个地址
函数的传参方式:
	1.复制传参
	2.地址传参
	3.全局传参(几乎用不到 全局参数可以直接使用)
定义两个函数: f1为值传递   f2为指针传递 都进行重新赋值操作
	void f1(int a)
	   int b = 10;
	    a = b;
	    printf("%p,%d\\n",&a,a);
	
	void f2(int *a)
	    int b = 10;
	    *a = b;
	    printf("%p,%d\\n",a,*a);
	
运行定义好的两个函数:
	int a = 100;
    f1(a);
    printf("%p,%d\\n",&a,a);       //传值
    int a2 = 100;
    f2(&a2);
    printf("%p,%d\\n",&a2,a2);     //传地址
输出结果:
	0x7ff7b315e94c,10
	0x7ff7b315e968,100
	0x7ff7b315e964,10
	0x7ff7b315e964,10
值传递 在函数内对形参的任何操作 不会影响实参 
地址传参:将实参的地址传递给形参,形参对保存的地址的内容进行操作,实参的值也会跟着变
地址传参若要改变参数的值 必须通过 *+地址去赋值 无论这个变量是什么类型
若要传递的实参本身是一个地址,例如字符串指针,则要传递指针的地址
例:
	int *str = "hello";
	f2(**p);

传递数组:
	传一维数组,由于数组名本身是一个首地址,所以传递时直接使用地址传递
	int a[10];
	f2(int*a);
	传二维数组,可以使用数组指针来传递
	int a[3][5];
	f2(int (*a)[5]);  
	传指针数组,可传指针数组的首地址,即传一个二级指针;
	char *p[3] = "what","the","fuck";
	f2(char **p);  
数组传参本质都是地址传递,所以在函数内部操作形参 都会改变实参

准备转行嵌入式,这阵子都是拿手在记笔记,忽然发觉手记的万一有改动不太好弄,且日后也不太好找出来看,文章都是个人心血来潮入坑嵌入式的学习笔记,下一篇c语言中关于指针的详解2 会继续记录指针与函数的关系以及结构体指针,文件指针
今天累了 先溜了~

C语言中关于指针的学习

指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; 能很方便地使用数组和字符串; 并能象汇编语言一样处理内存地址,从而编出精练而高效的程序。指针极大地丰富了C语言的功能。 学习指针是学习C语言中最重要的一环, 能否正确理解和使用指针是我们是否掌握C语言的一个标志。同时, 指针也是C语言中最为困难的一部分,在学习中除了要正确理解基本概念,还必须要多编程,上机调试。只要作到这些,指针也是不难掌握的。

       指针的基本概念 在计算机中,所有的数据都是存放在存储器中的。 一般把存储器中的一个字节称为一个内存单元, 不同的数据类型所占用的内存单元数不等,如整型量占2个单元,字符量占1个单元等, 在第二章中已有详细的介绍。为了正确地访问这些内存单元, 必须为每个内存单元编上号。 根据一个内存单元的编号即可准确地找到该内存单元。内存单元的编号也叫做地址。 既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。 内存单元的指针和内存单元的内容是两个不同的概念。 可以用一个通俗的例子来说明它们之间的关系。我们到银行去存取款时, 银行工作人员将根据我们的帐号去找我们的存款单, 找到之后在存单上写入存款、取款的金额。在这里,帐号就是存单的指针, 存款数是存单的内容。对于一个内存单元来说,单元的地址即为指针, 其中存放的数据才是该单元的内容。在C语言中, 允许用一个变量来存放指针,这种变量称为指针变量。因此, 一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。图中,设有字符变量C,其内容为“K”(ASCII码为十进制数 75),C占用了011A号单元(地址用十六进数表示)。设有指针变量P,内容为011A, 这种情况我们称为P指向变量C,或说P是指向变量C的指针。 严格地说,一个指针是一个地址, 是一个常量。而一个指针变量却可以被赋予不同的指针值,是变。 但在常把指针变量简称为指针。为了避免混淆,我们中约定:“指针”是指地址, 是常量,“指针变量”是指取值为地址的变量。 定义指针的目的是为了通过指针去访问内存单元。

      既然指针变量的值是一个地址, 那么这个地址不仅可以是变量的地址, 也可以是其它数据结构的地址。在一个指针变量中存放一
      个数组或一个函数的首地址有何意义呢? 因为数组或函数都是连续存放的。通过访问指针变量取得了数组或函数的首地址, 也就找到了该数组或函数。这样一来, 凡是出现数组,函数的地方都可以用一个指针变量来表示, 只要该指针变量中赋予数组或函数的首地址即可。这样做, 将会使程序的概念十分清楚,程序本身也精练,高效。在C语言中, 一种数据类型或数据结构往往都占有一组连续的内存单元。 用“地址”这个概念并不能很好地描述一种数据类型或数据结构, 而“指针”虽然实际上也是一个地址,但它却是一个数据结构的首地址, 它是“指向”一个数据结构的,因而概念更为清楚,表示更为明确。 这也是引入“指针”概念的一个重要原因。




文件型指针




#include<stdio.h>

int main()

{

  int *ptr;   // 声明一个int指针

  int val = 1;  // 声明一个int

  ptr = &val;  // 为指针分配一个int值的引用

  int deref = *ptr;  // 对指针进行取值,打印存储在指针地址中的内容

  printf("deref地址=%ld,=%d\n",ptr, deref);

}

2行,我们通过*操作符声明了一个int指针。接着我们声明了一个int变量并赋值为1。然后我们用int变量的地址初始化我们的int指针。接下来对int指针取值,用变量的内存地址初始化int指针。最终,我们打印输出变量值,内容为1

6行的&val是一个引用。在val变量声明并初始化内存之后,通过在变量名之前使用地址操作符&我们可以直接引用变量的内存地址。

8行,我们再一次使用*操作符来对该指针取值,可直接获得指针指向的内存地址中的数据。由于指针声明的类型是int,所以取到的值是指针指向的内存地址存储的int值。

 

 

指针与数组

#include<stdio.h>

int main()

{

         intmyarray[4] = {1,2,3,0};

   int *ptr = myarray;

   printf("ptr地址=%ld,*ptr=%d\n", ptr,*ptr);

   ptr++;

   printf("ptr地址=%ld,*ptr=%d\n", ptr,*ptr);

   ptr++;

   printf("ptr地址=%ld,*ptr=%d\n", ptr,*ptr);

   ptr++;

   printf("ptr地址=%ld,*ptr=%d\n", ptr,*ptr);

}

C语言的数组表示一段连续的内存空间,用来存储多个特定类型的对象。与之相反,指针用来存储单个内存地址。数组和指针不是同一种结构因此不可以互相转换。而数组变量指向了数组的第一个元素的内存地址。

一个数组变量是一个常量。即使指针变量指向同样的地址或者一个不同的数组,也不能把指针赋值给数组变量。也不可以将一个数组变量赋值给另一个数组。然而,可以把一个数组变量赋值给指针,这一点似乎让人感到费解。把数组变量赋值给指针时,实际上是把指向数组第一个元素的地址赋给指针。

 

 

指针与结构体

#include<stdio.h>

struct person {

  intage;

 char *name;

};

 

int main()

{

 struct person first;

 struct person *ptr;

 first.age = 21;

 char *fullname = "full name";

 first.name = fullname;

  ptr= &first;

 printf("age=%d, name=%s\n", first.age, ptr->name);

}

 

 

 

 

 

 

#include<stdio.h>

#include <stdio.h> 

#include <conio.h> 

#include <string.h> 

#include <stdlib.h>  

main()

{

  int count,*array; /*count是一个计数器,array是一个整型指针,也可以理解为指向一个整型数组的首地址*/

  if((array=(int *) malloc(10*sizeof(int)))==NULL)

   {

    printf("不能成功分配存储空间。");

    exit(1);

   }

  for (count=0;count<10;count++) /*给数组赋值*/

     array[count]=count;

  for(count=0;count<10;count++) /*打印数组元素*/

     printf("%d-",array[count]);

}

上例中动态分配了10个整型存储区域,然后进行赋值并打印。例中if((array=(int *) malloc(10*sizeof(int)))==NULL)语句可以分为以下几步: 
1)分配10个整型的连续存储空间,并返回一个指向其起始地址的整型指针 
2)把此整型指针地址赋给array 
3)检测返回值是否为NULL 
2free函数 
由于内存区域总是有限的,不能不限制地分配下去,而且一个程序要尽量节省资源,所以当所分配的内存区域不用时,就要释放它,以便其它的变量或者程序使用。这时我们就要用到free函数。 
其函数原型是: 
void free(void *p) 
作用是释放指针p所指向的内存区。 
其参数p必须是先前调用malloc函数或calloc函数(另一个动态分配存储区域的函数)时返回的指针。给free函数传递其它的值很可能造成死机或其它灾难性的后果。 
注意:这里重要的是指针的值,而不是用来申请动态内存的指针本身。例: 
int *p1,*p2; 
p1=malloc(10*sizeof(int)); 
p2=p1; 
…… 
free(p1) /*或者free(p2)*/ 
malloc返回值赋给p1,又把p1的值赋给p2,所以此时p1p2都可作为free函数的参数。 
malloc函数是对存储区域进行分配的。 
free函数是释放已经不用的内存区域的。 

 

 

malloc函数 
malloc函数的原型为: 
void *malloc (unsigned int size) 
其作用是在内存的动态存储区中分配一个长度为size的连续空间。其参数是一个无符号整形数,返回值是一个指向所分配的连续存储域的起始地址的指针。还有一点必须注意的是,当函数未能成功分配存储空间(如内存不足)就会返回一个NULL指针。所以在调用该函数时应该检测返回值是否为NULL并执行相应的操作。



心得体会:


堂上要讲授许多关于c语言的语法规则,听起来十分枯燥无味,也不容易记住,死记硬背是不可取的。然而要使用c语言这个工具解决实际问题,又必须掌握它。通过多次上机练习,对于语法知识有了感性的认识,加深对它的理解,在理解的基础上就会自然而然地掌握c语言的语法规定。对于一些内容自己认为在课堂上听懂了,但上机实践中会发现原来(转载自第。)理解的偏差,这是由于大部分学生是初次接触程序设计,缺乏程序设计的实践所致。

学习c语言不能停留在学习它的语法规则,而是利用学到的知识编写c语言程序,解决实际问题。即把c语言作为工具,描述解决实际问题的步骤,由计算机帮助我们解题。只有通过上机才能检验自己是否掌握c语言、自己编写的程序是否能够正确地解题。


以上是关于[学习笔记]C语言中关于指针的详解1的主要内容,如果未能解决你的问题,请参考以下文章

C语言 关于POINTER(指针)的问题, 急!!!!

C/C++学习笔记:智能指针详解

C/C++学习笔记:智能指针详解

C语言中关于指针的学习

C语言中关于指针的学习

c语言指针详解