【Linux基础与应用开发】数据结构--C语言4:指针和数组

指针

内存编号<=>内存地址<=>地址<=>指针
内存单元的地址称为指针,专门用来存放地址的变量,指针指针变量;在不影响,有时对地址、指针和指针变量不区分

-int *p
-int :指针所指向的类型
-int * :指针变量的类型
-p :指针变量的名字

-p :变量指针 内容是地址
-*p :指针所指对象,内容是数据
-&p :指针变量占用的存储区域的地址,是个常量

指针运算

-指针运算以指针变量所存在的地址量作为运算量而进行的运算
-实质是地址的运算
-只能进行赋值运算、算术运算和关系运算

算术运算:
px+n:指针向地址大的方向移动n个数据,(px)+sizeof(px的类型)*n
px-n:…小的方向移动n个数据
px++、++px:…大的方向移动1个数据
px–、–px:…小的方向移动1个数据
px-py:两指针之间相隔数据元素的个数,数据类型不同不能相减

指针与数组

一维数组的数组名位一维数组的指针(起始地址)指针常量

-x[i]、*p(x+i)、*(x+i)和px[i]具有完全相同的功能:访问数组第i个元素
#int include <stdio.h>
int main(void)
{
	int buf[]={3,3,4,5,6};
	int *p=buf;

	printf("buf[2] = %d\n",buf[2]);
	printf("buf[2] = %d\n",*(buf+2));
	printf("buf[2] = %d\n",p[2]);
	printf("buf[2] = %d\n",*(p+2));
	return 0;
}
-指针变量和数组在访问数组中元素时,一定条件下有相同形式,因为指针变量和数组名都是地址量
-二者本质上是不同的,指针变量时地址变量,而数组的指针时地址常量
#int include <stdio.h>
int main(void)
{
	int buf[]={3,3,4,5,6};
	int *p=buf;

	printf("&buf+1=%p\n",&buf+1);	// 取buf地址加一相当于加了整个数组的长度,也就是地址+20
	return 0;
}

指针与二维数组

二维数组连续存储,按行优先存储

// 使用一级指针遍历二维数组
#include <stdio.h>
int main(void)
{
	int buf[2][3]={{1,2,3},{4,5,6}};
	int *p=(int *)buf;

	for(int i=0;i<6;i++)
	{
		printf("%d\n",*p++);
	}
	return 0;
}

行指针
-存储类型 数据类型 (*指针变量名)[表达式] int a[2][3] int (*p)[3]
-int *p[3]是一个可以存放三个int *整型地址元素的数组

#include <stdio.h>
int main(void)
{
	int buf[2][3]={{1,2,3},{4,5,6}};
	int (*p)[3]=buf;
	// 以下都是指buf[1][1]=5
	printf("buf[1][1]=%d\n",buf[1][1]);
    printf("buf[1][1]=%d\n",(*(p+1))[1]);

    printf("buf[1][1]=%d\n",*(p[1]+1));
    printf("buf[1][1]=%d\n",p[1][1]);
    printf("%d",*(*(p+1)+1));

	printf("%d",(int *)p[1][1]);
	return 0;
}

字符数组

char数据类型的指针边变量称为字符型指针变量

-char str[]="Hello";  char *p=str;	
-char *str="hello";
// 初始化字符指针是把字符串的首地址赋予指针,并不是把字符串复制到指针中。前者存在于内存数据区,在栈里;二后者在只读取,无法修改或处理
#include <stdio.h>
#include <string.h>
// 指针实现字符串连接
int main(void)
{
	char buf1[20]="hello";
	char buf2[]=" world";
	char *p=buf1;
    char *q=buf2;

    while(*p)
        p++;
    while(*p++=*q++)
        NULL;
    
	printf("%s\n",buf1);
	return 0;
}

指针数组

具有相同存储类型和数据类型的指针变量构成的集合,存储类型 数据类型 *指针数组名 [];指针数组名表示该指针数组的起始地址,int *p[3]注意和数组指针(行指针)的不同

// 利用指针处理二维数组,计算出所有元素的和
#include <stdio.h>
#include <string.h>
// 利用指针处理二维数组,计算出所有元素的和
int main()
{
	int a[2][3]={{1,9,4},{9,1,6}};
    int *p[2];
    int i,j,sum;

    p[0]=&a[0][0];
    p[1]=&a[1][0];

    for(i=0;i<2;i++)
    {
        for(j=0;j<3;j++)
        {
            printf("%d %d ",a[i][j],*(p[i]+j));
            sum+=(*(p[i]+j));
        }
    }
    printf("sum=%d\n",sum);
    printf("%d\n",sizeof(p));
	return 0;
}

多级指针

-把一个指向指针变量的指针变量称为多级指针
-对于指向处理数据的指针变量称为一级指针变量,一级指针
-指向一级指针的指针变量称为二级指针变量,二级指针
-二级指针表述:存储类型 数据类型 **指针名

多级指针的运算

指针变量加1,向地址大的方向移动一个目标数据

int main(void)
{
	char *ps[3]={"aaa","bbb","ccc"};
	char **pps=ps;

    printf("ps=%s\n",ps[0]);
    printf("pps=%s\n",*(pps+1));    // 取第一个元素aaa
    printf("%c\n",**pps);   // 取第一个字符串元素的第一个字符

    return 0;
}

void指针

void指针是一种不确定数据类型额指针变量,可以通过强制类型转换让该变量指针指向任何数据类型的变量

-void *指针变量名称
-对于void指针,在没有强制类型转换之前,不能进行任何指针的算术运算(不确定)
#include <stdio.h>
#include <string.h>
// 学习地址:https://www.bilibili.com/video/BV1bo4y1Z7xf?p=2&vd_source=0d3fa7725ce01ed5e63fd14745adb26b
void fun1(int x){
    x = x + 1;
}
void fun2(int *y) {
    *y = *y + 1;
}

int sumofelements(int a[], int size) {
    int i, s = 0;
        // int size = sizeof(a) / sizeof(a[0]);    
            // 在主函数外的a实际上与主函数内的a是不同的,虽然名字相同但是他们在stack的不同地方
            // 也就是说主函数的a实际长度为20,而这里的a长度为4,类似于一个整型指针
            // 编译器只是复制了主函数数组a的首地址给到主函数外的a
            // 在这里编译器把int a[]隐式地处理成int a而不是数组
            // 数组可以很大,所以不会拷贝整个数组到函数外,这样资源消耗太大
            // 数组一般不用传值方式,而是传引用
    for (i = 0; i < size; i++) {
        s += a[i];
    }
    return s;
}
void print(char *C) {
    int i = 0;
    while (C[i] != '\0'){
        printf("%c", *C);
        C++;    // 或者这里用 C++ 实现数组自增,因为在print函数里C相当于指针
    }
    printf("->end\n");
}

void phw() {
    printf("hello world!\n");
}
int *Add(int* a, int* b) {    // 不同于上面的Add,这里传的是a的地址

    int c = (*a) + (*b);

    return &c;
}
int add(int *a,int *b) {   
    printf("address of a in add = %d\n", &a);   // 传值
    printf("address of a in add deliver by pointer = %d\n", a); // 传地址
    printf("value at address stored in a of add = %d\n", *a);
    
    
    int c = (*a) + (*b);

    return c;
}

void printhello(const char *name) {
    printf("hello %s\n", name);
}

void A() {
    printf("hello");
}
void B(void (*ptr)()) {
    ptr();
}

int main()
{
/*  // 指针指向问题
    int a = 10;
    int* p;
    p = &a; // 此处将a的地址赋予p
    printf("%d\n",*p);

    *p = 20;
    printf("%d\n", a);

    int b=30;
    *p = b; // 和p=&a不同,这里p没有链接到b的地址,所以改变p的值并不会改变b的值
    *p = 40;
    printf("%d\n", b);
*/
/*  // 地址与指针
    int a = 10;
    int* p;
    p = &a;
    printf("address p is %d\n",p);
    printf("value at address p is %d\n", *p);
    printf("size of integer is %d bytes\n", sizeof(int));
    printf("address p+1 is %d\n", p+1);
    printf("value at address p+1 is %d\n", *(p+1));

    printf("value of *p+1 is %d\n", *p+1);
*/
/*
// 指针类型转换,void指针——通用指针类型,指针算术
// 既然指针只存放地址,为什么需要不同类型的指针?因为这样就可以访问和修改这些地址对应的值
// 不同数据有不同大小,int有4bytes,char-1,float-1,用户自定义的类和结构体又有不同大小
    int a = 1025;
    int *p;
    p = &a;
    printf("size of integer is %d bytes\n", sizeof(int));
    printf("address of p = %d, value of p = %d\n",p,*p);
    printf("address of p+1 = %d, value of p+1 = %d\n", p + 1, *(p + 1));

    char *p0;
    p0 = (char *)p; // 直接p0 = p会因为类型不对应报错,所以需要类型转换
        // 此处p0指向字符的指针,而p是指向整型的指针
    printf("size of char is %d bytes\n",sizeof(char));
    printf("address of p0 = %d, value of p0 = %d\n", p0, *p0);
    // 这里*p0的值输出来1,是因为1025在二进制上是00000000 00000000 00000100 00000001
    // 强制转换后最右边的1个字节的地址输入到char指针p0中也就是00000001
    // 所以最后只剩下*p0 = 00000001 所以*p0 = 1
    printf("address of p0+1 = %d, value of p0+1 = %d\n", p0 + 1, *(p0 + 1));
        // 这里*(p0+1)也就是变成00000100,所以值变成4

    printf("\n################################\n \n");

    void *p1;    // 定义一个通用型指针,不能直接对他解引*p0,只可以读取地址
    p1 = p;
    printf("address of p1 = %d\n",p1);

*/
/*
// 指向指针的指针
    int x = 5;
    int *p = &x;
    *p = 6;
    int **q = &p;
    int ***r = &q;

    printf("p = %d\n",*p);
    printf("*q = %d\n", *q); // 存放p的地址
    printf("**q = %d\n", *(*q));  // p的值

    printf("**r = %d\n", **r);  // 存放q的地址
    printf("***r = %d\n", ***r);    // x的值

    ***r = 10;
    printf("x = %d\n",x);

    **q = *p + 2;
    printf("x = %d\n", x);
    printf("r = %d\n", ***r);
*/
/*
// 指针为函数传值和传引用,上面有两个void函数fun1 fun2是这里下面
    int a = 5, b = 6;
    fun1(a);
    printf("new a is = %d\n",a);
    fun2(&b);
    printf("new b is = %d\n", b);
*/
/*
// 指针与数组
    int a[] = { 2,4,5,8,1 };
    for (int i = 0; i < 5; i++) {
        printf("address = %d\n", a+i);  // 数组本身就像指针,有些操作相同
        printf("address = %d\n", &a[i]);
        printf("value = %d\n", a[i]);
        printf("value = %d\n", *(a+i));

    }

    int* p = a;
    p++;    // 不能a++
    printf("%d\n",*p);
*/
/*
// 数组作为函数参数
    int a[] = { 1,2,3,4,5 };
    int size = sizeof(a) / sizeof(a[0]);
    int total = sumofelements(a, size);

    printf("sun of the array = %d\n",total);
*/
/*
// 指针和字符数组
    char c[] = "JOHN";
    printf("size of c in bytes is : %d\n",sizeof(c));
    int len = strlen(c);
    printf("length of c is : %d\n", len);   // 长度不回包含结束符\0
    char b[] = { 'J','O','H','N','\0' };    // 这个时候结束符\0就不再是隐式的了
    printf("size of b in bytes is : %d\n", sizeof(b));
    int len1 = strlen(b);
    printf("length of b is : %d\n", len1);   // 长度不回包含结束符\0

    char C[20] = "Hello";
    print(C);  // 上面定义一个能够输出C的函数print,用于当输出到\0时停止输出
    // 在一些严格的编译器中,这行printf其实是将长度为20的整个数组都打印出来
    // 在函数print中,并不知道数组长度,但是只要读到\0就会停止输出
*/
/*
// 指针和二维数组、多维数组
    int c[3][2][2] = { 
        {{2,5},{7,9}},
        {{3,4},{6,1}},
        {{0,8},{11,13}}};

    printf("%d %d %d %d\n", c, *c, c[0], &c[0][0]);
    printf("%d",*(c[0][0]+1));
*/
/*
// 指针作为函数返回
    //int x = 2, y = 4;
    //int z = Add(x, y);  // 函数Add,局部变量,无法访问main函数中的变量

    //printf("sum = %d\n", z);

    int a = 2, b = 4;

    phw();

    int* ptr = Add(&a, &b);
    printf("ptr = %d\n", *ptr);

    int c = add(&a, &b);
    printf("address of a in main = %d\n", &a);
    printf("sum = %d\n", c);
 */
/*
// 函数指针,存储函数的指针,下面代码课程是可以运行的
    void (*ptr)(char*);

    ptr = printhello;
    ptr("tom");
*/
    // 指针的使用案例,回调函数
    // void (*p)() = A;
    // B(p);
    B(A);   // 以上两行代码可以用这个表示

    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值