当前位置: 首页 > article >正文

C语言——指针基础

1 指针基础

怎么获得变量地址

在这里插入图片描述

1   如何产生一个指针变量
	——>类型* 标识符;
	int* p1;
	char* p2;
	double* p3;
	//不同类型的基本指针占用内存是一样的都是4个字节(32位)/8个字节(64位),都是存的地址


2 数组名是数组首地址但不是普通指针
  ——>数组名绑定的一段内存可以影视转化为指针
  int array[4] = { 1,2,3,4};
	printf("array:%zd\n", sizeof(array));//打印16,整个数组的大小
	printf("array:%p\tarray[0]:%p", array, &array[0]);//值一样,内容不一样


3  int*p1;未初始化是野指针
   p1=NULL;变成空指针。
   NULL-->(void*)00强制转化地址
   一般定义指针 int*p=NULL;


4 指针取值运算
 int num=666;
 int*P=#
 //*指针变量 ,指针变量[0];
 printf("%d\t%d\t%d\n", num, *pnum, pnum[0]);//都打印666
 *pume=888printf("%d\t%d\t%d\n", num, *pnum, pnum[0]);//都打印888
 //操作指针变量*pume就相当于操作num,任意修改一个其他都会跟着变

5 指针偏移——>移动数据位
 //p+n或者p-n是移动数据位
 //int* p
 要明白指针类型
 //指针的类型:int*  -->去掉变量名
 //指针的所指向类型:int-->操作的数据的类型-->去掉变量名和*号(基类型)
 //知道操作的数据类型就知道偏移多少
 printf("p1=%p\n", p1);     int p1=0000000000000000
 printf("p2=%p\n", p2);		char p2=0000000000000000
 printf("p3=%p\n", p3);  	double p3=0000000000000000
 //+1,偏移一个数据位   
 printf("p1=%p\n", p1+1);  	int p1=0000000000000004
 printf("p2=%p\n", p2+1);   char p2=0000000000000001
 printf("p3=%p\n", p3+1);   double p3=0000000000000008
   
 printf("array=%p\n", array);   array=000000A8A24FF4C8
 printf("array=%p\n", array+1); array=000000A8A24FF4CC //差4
    
 int(*p)[3] = NULL;    
 printf("p=%p\n", p );	  //操作的是int[3]
 printf("p=%p\n", p + 1);  //int[3]——>偏移一个数组12个字节

2 const与指针变量

const后面不能被修改,把变量变成了常属性。 
    
1.以下两种没区别
const int cnum1 = 0;
int const cnum2 = 0;

2.*前面,指针指向内容不可以修改——>修饰1
//以下两种没区别
//const描述的是指针所指向的内容
const int* p1 = &cnum1;
int const* p2 = &cnum1;
int data = 0;
*p1=666;(不行)
p1 = &data;
p1 = p2;(可以)

3*后面,指针不可以修改——>修饰2
 //让指针变量指向的地址固定
int* const p3 = &data;
//const修饰的是p3 不可修改
p3 = p2; (不行)
p2 = p3;(可以)
    
4const int* const p4 = &data;
//都不能被修改

在这里插入图片描述

3 二级指针与多级指针

因为指针变量也有地址,也可以用指针来存放
类似与套娃
int num = 0;
int* p1 = #
int** p2 = &p1;
int*** p3 = &p2;

num = 999;
printf("%d\n", ***p3);
printf("%d\n", p3[0][0][0]);//这两个也一样都打印999

4 指针操作一维数组

指针指向数组首地址,直接当数组名去用

1
    int array[4] = { 1,2,3,4 };
	int* p = array;
	//p = &array[0];
	for (int i = 0; i < 4; i++)
{
	printf("%d\t", p[i]);		//推荐用法
	//printf("%d\t", *(p + i));
	//printf("%d\t", (p + i)[0]);
}

	//下面用法不推荐,但是要能看懂
	//改变指针指向,指针偏移到数组之后,任意越界用起来危险
	while (p != array + 4)
{
	printf("%d\t", p++[0]);
}
	printf("\n");

2 负下标,不用首地址做初始化
    
int data[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* pp = &data[5];
printf("%d\n", pp[0]);
for (int i = -4; i < 0; i++) 
{
	printf("%d\t", pp[i]);
}
printf("\n");



3  函数传参,传输组名等效于传一级指针(数字类必须还要传长度)
   在 C 语言中,数组作为函数参数时,实际上是传递数组的首地址,而不是整个数组。
   因此,函数内部无法直接获取数组的长度。这就是为什么我们需要额外传递数组的长度(元素数量)的原因
    
    void print_array(int array[], int arrayNum) 
{
	for (int i = 0; i < arrayNum; i++) 
	{
		printf("%d\t", array[i]);
	}
	printf("\n");
}
void print_array_2(int *array, int arrayNum)
{
	for (int i = 0; i < arrayNum; i++)
	{
		printf("%d\t", array[i]);
	}
	printf("\n");
}


4 传字符串一定要写const,C语言里可能没有影响,c++有影响(养成好习惯)
int my_strlen(const char* str) 
{
	int count = 0;
	while (str[count] != '\0')
		count++;
	return count;
}

5 指针操控二维数组

1  一级指针操作二维数组——>列转换为序号
    void test_one() 
{
	int array[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int* p = &array[0][0];
	for (int i = 0; i < 3; i++) 
	{
		for (int j = 0; j < 4; j++) 
		{
			//行列转换为序号
			printf("%d\t", p[i * 4 + j]);
		}
		printf("\n");
	}
    
2  数组指针操作
    
    void test_two() 
{
	int array[3][4] = { 1,2,3,4,5,6,7,8,9,10,12 };
	//二维数组名偏移的一行
	printf("array:%p\n", array);
	printf("array+1:%p\n", array+1);  //16
	//二维数组名+一个下标 转换为一个一级指针
	printf("array[0]:%p\n", array[0]);
	printf("a rray[0]+1:%p\n", array[0]+1); //4;

    
    
	2.1//推荐用法!!! 
	//数组指针操作二维数组(直接当数组名)
	int(*p)[4] = array;
	for (int i = 0; i < 3; i++) 
	{
		for (int j = 0; j < 4; j++) 
		{
			printf("%d\t", p[i][j]);
		}
		printf("\n");
	}
    
    
    
    以下是多种写法,用A换元思想(书本上出现要认识)
	2.2 for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			printf("%d\t", *(*(p+i)+j));
			//p[i]==>A  A[j]==>*(A+j)
			//p[i]==>*(p+i)==>A
		}
		printf("\n");
	}
    
	2.3		printf("%d\t", *(p[i] + j));
			//p[i]==>A  A[j]==>*(A+j)
			//p[i]==>A
		
	2.4		printf("%d\t", ((p + i)[0] + j)[0]);
			//p[i]==>A  A[j]==>*(A+j)==>(A+j)[0]
			//p[i]==>(p+i)[0]==>A
	
	2.5		printf("%d\t", (p[i] + j)[0]);
			//p[i]==>A  A[j]==>*(A+j)==>(A+j)[0]
			//p[i]==>(p+i)[0]==>A

    
    
    
 3  函数传参
     void print_array(int(*p)[4], int row, int cols)
{
	for (int i = 0; i < row; i++) 
	{
		for (int j = 0; j < cols; j++) 
		{
			printf("%d\t", p[i][j]);
		}
		printf("\n");
	}
}
void test_three() 
{
	int array[3][4] = { 1,2,3,4,5,65,7,8,9,0,19,12 };
	print_array(array, 3, 4);
}

6 万能指针

#include <stdio.h>
#include <stdlib.h>
int main() 
{
    
//万能指针
void* p = NULL;
int num = 999;
p = &num;

//万能指针不能*p直接访问
//要做目标数据类型的强制类型转换
printf("%d\n", *(int *)p);
double dNum = 8.98;
p = &dNum;
printf("%.2lf\n", *(double*)p);
    
return 0;

7 指针函数

#include <stdio.h>
#include <stdlib.h>.	传指针问题
        
1	//值传递
	void modify_one(int a)  //int a=实参
	{
		a = 100;
	}
	//没有返回值,打印出来还是0


	//址传递(函数不需要返回值就可以修改值)
2	//修改实参的值 ,传入实参的地址,修改是*地址
	void modify_two(int* p) //int* p=实参 
	{
		*p = 100;
	}

3	//(为什么传二级指针)修改指针指向num指向g_num,用二级指针接收指针的地址
    int g_num = 999;
	void modify_point(int** p)   //int** p=实参
	{
		*p = &g_num;
	}
//——————————————————————————————————————————————
int main(){
  1 int num = 0;
	modify_one(num);
	printf("%d\n", num);
//——————————————————————————————————————————————    
  2 modify_two(&num);
	printf("%d\n", num);
//——————————————————————————————————————————————   
  3	int* p = &num;
	modify_point(&p);//传入的指针变量
	printf("%d\n", p[0]);
}

.	返回指针问题
        
1	//C语言不允许返回局部变量的地址(不安全的)
    //内存会回收,变量地址给其他不知道的东西了
	//warning C4172: 返回局部变量或临时变量的地址: num
	int* get_num() 
	{
		int num = 99999;
		return &num;(不行)
	}

2 	//需要学会区分什么叫做局部变量的地址
	int* get_num_one(int* p) 
	{
		return p;(可以)——>因为在主函数里调用时,变量一直到结束时都是有效的
	}

3	//可以返回堆区内存的地址(下章说)

4	//返回一个数组的函数(返回数组首地址就可以了)
	int array[3] = { 1,2,3 };
	int* get_array() 
	{
		return array;
	}

int main() 
{
  1 int* pp = get_num();
	printf("%d\n", *pp);
	printf("%d\n", *pp);
//——————————————————————————————————————————————    
  2 get_num_one(p);//函数返回指针后还可以的继续操作
	//p[0];
	get_num_one(p)[0];
	//*p=199;
	*get_num_one(p) = 199;
	printf("%d\n", g_num);
//——————————————————————————————————————————————
  4 int* parray = get_array();
	for (int i = 0; i < 3; i++) 
	{
		printf("%d\n", parray[i]);
	}
	printf("\n");
	return 0;
}

在这里插入图片描述

!!!这里先看一下指针函数和函数指针的区别,下篇会将到函数指针,容易弄混!!!

在这里插入图片描述

在这里插入图片描述


http://www.kler.cn/a/418488.html

相关文章:

  • springboot366高校物品捐赠管理系统(论文+源码)_kaic
  • AI开发-数据可视化库-Seaborn
  • Flink四大基石之Time (时间语义) 的使用详解
  • 数据结构-查找(四)总结与对比
  • Spark Optimization —— Reducing Shuffle
  • 精准用户获取与私域流量运营:多商户链动 2+1 模式商城小程序的赋能策略
  • MySQL中什么是脏读、幻读、不可重复读
  • 使用java操作Parquet文件
  • http(请求方法,状态码,Cookie与)
  • C#高级教程
  • Arrays.asList()新增报错,该怎么解决
  • 云原生周刊:K8s 严重漏洞
  • AI智能体与代理IP:携手共创智能网络新时代
  • ts解决vite unplugin-auto-import/vite
  • 【自动化】配置信息抽取
  • C#VB.NET开发整体一键国际化显示
  • 银河麒麟v4/v10 Ubuntu上添加服务过程-以编译postgressql数据库为例
  • 【论文复现】基于BERT的语义分析实现
  • 深度转点云 点云着色
  • springboot+jsp二级页面无法请求
  • NLP 的研究任务
  • c++类和对象(2)
  • 前端http,ws拉流播放视频
  • 多种平台上安装部署调试Open5GS(二)
  • 嵌入式C语言技巧11:当系统崩溃遇上断电危机:如何确保嵌入式系统的绝对安全?
  • 杰发科技AC7803——不同晶振频率时钟的配置