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*)0 把0强制转化地址
一般定义指针 int*p=NULL;
4 指针取值运算
int num=666;
int*P=#
//*指针变量 ,指针变量[0];
printf("%d\t%d\t%d\n", num, *pnum, pnum[0]);//都打印666
*pume=888;
printf("%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 = #
//万能指针不能*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 = #
modify_point(&p);//传入的指针变量
printf("%d\n", p[0]);
}
二. 返回指针问题
1 //C语言不允许返回局部变量的地址(不安全的)
//内存会回收,变量地址给其他不知道的东西了
//warning C4172: 返回局部变量或临时变量的地址: num
int* get_num()
{
int num = 99999;
return #(不行)
}
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;
}
!!!这里先看一下指针函数和函数指针的区别,下篇会将到函数指针,容易弄混!!!