基于Linux系统的C语言 ---------指针篇

指针:  C语言的灵魂  


  
    指针是啥? 


    1. 指针变量    :   一个变量  存放一个内存的地址   
    2. 一个内存地址   指针常量 

指针变量的定义: 
    存储类型  指针指向的空间的数据类型 *  变量名;
示例: 
    int *p = &a;

指针变量的 赋值:  类型匹配才可以赋值 
    物理意义: 与指定的内存  建立联系 

&地址运算:   变量可取地址  有内存的量
    得到变量的内存地址   

野指针:  一个指针  指向的内存 不可知 或  不确定 或  非法(不可访问) 
空指针:  一个指针  指向内存 0地址(不允许访问)  即这个指针中的值是0   int *p = NULL;
宏 NULL 空指针   (void *)0  通常在程序中 表示一个指针 没有建立任何指向关系 

void * 类型 : 表示 万能指针, 一个可以指向 任意类型的指针 

指针的访问:  * 指针取值运算符     
   *指针变量  即等同于 指针指向的目标(对象)
    
    1  * 只能对指针操作 只能对有具体类型的指针 
    2 指针必须有建立指向关系   不能对空指针 野指针进行*操作 
            *空指针  段错误/空指针异常
            *野指针  结果未知   大概率段错误 
            void * 类型的指针  可以赋值  void *p = &a;    *p 不行   *(int*)p 可以的
           不能*操作   通常是进行强制类型转换 为指定类型的  
int b;
int *p = &b;
*&b; 等同于 b 
      * 是 & 的逆运算 
&*p; 等同于 &b
      & 是 * 的逆运算  

指针的算数运算: 
    指针 +或- 一个整数    其结果仍然是一个指针 
        其运算本质就是  地址的移动 + 向大地址方向移动  - 小地址方向移动 
        其移动的字节大小  由其指向的类型长度决定  +1 / -1 移动一个指向的类型 
    指针的 ++ -- 运算    p++; *p++; (*p)++; 

#include <stdio.h>

int a =5;
int main()
{

	int b;
	int *p = &a; //定义一个指针变量并初始化
	int **pp = &p;
	int *x;  // 野指针
	x = NULL;// 空指针
	//指针的表示
	printf("&a=%p\n",&a);
	printf("p= %p\n",p); // 打印变量p中存放的地址
	p = &b; //给p赋值
	printf("p= %p\n",p); // 打印变量p中存放的地址	
	printf("&p=%p\n",&p); // 打印变量p的地址	
	printf("pp=%p\n",pp); // 打印变量pp中存放的地址	

	//指针的访问
	*p = 20; //*p 等同于b *p = 20;  等同于 b = 20;  给*p赋值
	printf("b=%d\n",b);
	printf("*p=%d\n",*p);

	printf("**pp=%d\n", **pp); // **pp 等同于 *p 等同于b
	printf("*pp=%p\n", *pp);  // *pp 等同于p 
	printf("*&b=%d\n",  *&b ); 
	printf("&*p=%p\n",  &*p ); 
	//空指针
	//int *q = NULL;
	//printf("%d",*q);  段错误
	//void * 
	void *q = p;
	printf("q=%p\n",q);
	//printf("*q=%d\n",*q); //语法错误,无法编译
	printf("*(int*)q=%d\n",*(int*)q); //
	return 0;
}


    指针 与 指针的 运算 


    1. 大地址 - 小地址  结果是一个整数  其大小为 这两个地址之间相隔的 指向的类型个数 
        (1)  这两个地址 必须在同一段连续的内存空间上  
        (2)  这两个地址 类型要一致 
    
    2. 关系运算   本质是 判断 这个两个地址 在内存中的位置  
    p  >  q   p地址 在 q地址的 大方向  
    p  <  q   p地址 在 q地 址的 小方向
    p ==  q   p和q指针指向同一个内存 
    p !=  q   p和q指针指向不同的内存 
    p  >= q   p地址 在 q地址的 大方向  或 相同
    p  <= q   p地址 在 q地址的 小方向  或 相同
    
一级指针和一维数组: 
    数组名可以当指针用 
    指针变量 可以做数组用 
    区别在于  指针变量 可以重新赋值   而数组名不行 
    int arr[5] = {1,2,3,4,5};
    int *p = arr; // p = &arr[0]  等同 
    p[0]  == arr[0]  == *arr  == *p    //等同 
    p[1]  == arr[1]  == *(arr+1)  == *(p+1)
    

有如下定义:
int a[10]={0,1,2,3,4,5};
int *p = a;
假设每个表达式中p=a;求表达式代表的值?
*p=?    a[0]   == 0 
*a=?    a[0]   == 0
p[0]=?  a[0]   == 0 
a[0]=?  
*p++=?  ==  *(p++) == 先*p用 作为表达式的值, 然后 p=p+1
*(p++)=?
*p+1=?     a[0] + 1 == 1
*(p+1)=?   a[1]  == 1
*a+1=?     a[0] + 1 == 1
*(a+1)=?   a[1]  == 1
a++;   不行  a数组名  常量指针 不能赋值


练习: 数组倒叙 
void arr2rra(int *start, int *end)   //倒叙 从start到end之间的 数组元素, 不能定义其他变量
{

}
int main()
{
    int arr[] = {1,2,3,4,5};
    show_arr(arr,5);
    arr2rra(arr, arr+4);
    show_arr(arr,5);
    return 0;
}

#include <stdio.h>
void show_arr(int *arr, int len )
{
for (len=len-1;len>=0;len--)
  printf("%d",arr[len] );
}

int main()
{
int arr[]={1,2,3,4,5};
show_arr(arr,5);
printf("\n");

return 0;
}

指针与字符串

char s[] = "hello";  //s是一个数组 将"hello"复制一份到s数组中

cosnt char *p = "hello";  // p是一个指针变量 指向常量区字符串 
    *p 

求字符串的长度:
int len(const char *s)
{
    char *p = s;
    while(*p) p++;
    return p-s;
}

const 修饰指针: 
    cosnt char  *p;              *p 只读   p 可读可写 
    char * const p;              *p 可读可写  p 只读 
    const char * const p ;      *p 只读   p 只读 


 

总结指针的 5个运算:
    =  赋值运算  建立指向关系  
    *  取指向对象     *p 
    +/- n  地址移动
    p-q  指针相减 
    p>q  关系运算 

指针占用的内存大小: 
    指针变量指向的类型 不影响其 占用的内存大小  
    若 是32位机器   地址大小为 4字节 
    若 是64位机器   地址大小为 8字节 

指针数组 与 二级指针 

指针数组: 就是一个数组其元素是  指针 
    如图所示 : 
    ps 是一个  常量字符串指针数组 
    定义:
    存储类型  元素类型   数组名[元素个数] = {};
    auto     cosnt char * ps[6] = {"Beijing", "Moscow", "New York", "","", NULL};

    遍历指针数组:
    for(int i= 0; i<5; i++)
        printf("%s ",ps[i]);

    指针方式遍历:
//二级指针
const char ** pps = ps;

    //遍历指针数组:
    for( int i = 0; i<5; i++)
        printf("%s ",*pps++);
    printf("\n");
    

#include <stdio.h>

//函数遍历任何二维数组
void show(int *arr, int hang, int lie)
{
	for(int i=0;i<hang;i++)
	{
		for(int j=0;j<lie;j++)
		  printf("%d ",arr[ i * lie +j ]);
		printf("\n");
	}
}
//函数遍历任何二维数组
void show2(int *arr, int hang, int lie)
{
	//了解
	int (*p)[lie] = (int (*)[lie])arr;
	for(int i=0;i<hang;i++)
	{
		for(int j=0;j<lie;j++)
		  printf("%d ",p[i][j]);
		printf("\n");
	}
}



int main()
{
	//数组指针 与  二维数组:
	//二维数组:
	int arr[2][3] = {1,2,3,4,5,6};
	//数组指针:  含义: 一个指针 指向一个一维数组 
	int	(*p)[3] = arr;  // p = &arr[0]; 	
	printf("p=%p\n",p);
	
	printf("&arr[0]=%p\n",&arr[0]);
	printf(" arr   =%p\n", arr);
	printf(" arr[0]=%p\n", arr[0]);

	//通过p遍历数组arr
	for(int i=0;i<2;i++)
	{
		for(int j=0;j<3;j++)
		  printf("%d ",*(*(p+i)+j));
		printf("\n");
	}
	
	//二维数组 展平遍历
	int * q = (int*)arr;	
	for(int i=0;i<6;i++)
	  printf("%d ", *q++);
	printf("\n");

	show2((int*)arr, 2, 3);

	return 0;
}


练习: 
char *a[3] = {"hello","world","ok"};
char **p = a;
 
 假设每个表达式中p=a;求表达式代表含义或值?
a[0] == "hello" == char*  == %s 
*p   == a[0]  
**p  == *a[0] == 'h'
*a   == a[0]
**a  == *a[0] == 'h'
*p++ == a[0] 然后 p=p+1
*(p++) == *p++
**(p+1)) ==  *"world" == 'w'
 
*(*(p+1)+1)  == *(a[1]+1) == 'o'
 
(*p)++  考虑p能否进行该操作? 可以操作 
    先*p == a[0]  然后  a[0] = a[0]+1; a[0] ---> "ello"

数组指针 与  二维数组:
二维数组:
int arr[2][3] = {1,2,3,4,5,6};
数组指针:  含义: 一个指针 指向一个一维数组 
int    (*p)[3] = arr;  // p = &arr[0]; 

&arr[0]=0x7ffc4307c1a0   // 数组指针类型  
 arr   =0x7ffc4307c1a0   // 数组指针类型 
 arr[0]=0x7ffc4307c1a0   // 常量int类型指针 
&arr[0][0] =0x7ffc4307c1a0 // 常量int类型指针  

问p+1是什么?  p+1  == arr[1]
练习:
int a[2][3];
int (*p)[3] = a;
 
假设每个表达式中p=a;求表达式代表含义?
a[0]    0号元素是一个一维数组 
*p      == a[0]
**p     == a[0][0] 
*a      == a[0]
**a     == a[0][0] 
*p++    == 先*p == a[0] 然后  p = p+1;  p----> a[1]
*(p++)  == *p++  
**(p+1) == *a[1] == a[1][0]
 
*(*(p+i)+j)
        ==  *(a[i]+j)  == a[i][j]
        
(*p)++  考虑p能否进行该操作?  不能  
    先*p == a[0] 然后  然后  a[0] = a[0]+1;  数组名不能赋值 

指针与函数: 
    指针函数:
        本质是一个函数, 返回值是一个指针 
    
    函数指针:
        本质是一个指针, 指向一个函数 

代码段: 
    存储代码 

    函数名:
        1. 代指这个函数本身
        2. 代指这个函数的首地址 
    


函数类型: 


    除去函数名 以及形参名等 名字  剩下的就是函数的类型 

函数指针定义:
    指向的函数返回值类型    (*函数指针名)(函数形参类型列表) = 对应类型的函数;
    
函数指针应用:  回调函数 多用于写接口  
接口: API 
回调函数: 一个函数 其参数 有函数指针类型  

    
案例:  要求写一个函数,  参数传入一个数组, 要求输出 满足调用者条件的数? 
void    func1(int arr,int len, ????)
    
创建线程的函数:  了解 
           int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);
    
void *(*start_routine) (void *):
    
    start_routine 是一个指针  指向一个函数  该函数类型 为 void * (void *)
    void * (void *) 是 一个函数类型  这个类型的函数 有一个 void*类型的返回值 
                        有一个 void * 的形参 

#include <stdio.h>

//函数指针
int func(char a)// 函数类型 int (char)
{
	printf("hello func\n");
}

//定义一个函数指针 存放函数的地址
int (*fp)(char) = &func; 

int main()
{
	(&func)('A');
	printf("func=%p\n",func);
	printf("&func=%p\n",&func);
	printf("fp  =%p\n",fp);
	printf("main=%p\n",main);
	//通过函数指针 调用指向的函数
	(*fp)('B');
	fp('B');


	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值