C指针

数据的存储

  • 计算机将内存分隔为字节(byte),每个字节可存储8位(bit)的二进制信息。每个字节都有唯一的地址,用来和内存中其他字节相区别。一个字节称为一个存储单元(内存单元)。
  • 为了正确访问内存单元,每个内存单元都有一个编号,内存单元的编号称为地址。
  • 内存单元中的内容是我们关注的数据
4933701-25c580283306b3cb.png
数据的存储

访问方式

  • 数据访问分为直接访问和间接访问
  • 直接访问指的是直接访问内存单元中的内容,间接访问时通过内存单元编号(地址)以及数据所占字节数来访问内存中的数据。
  • 间接访问在程序中随处可见,指针是间接访问的常用方式。

指针变量

  • 理解指针的第一步是在机器级别上观察指针表示的内容,指针是内存中数据的内存地址。
  • 指针就是地址,而指针变量就是存储地址的变量。
4933701-a769353a28999412.png
指针变量
  • 通过&符号取变量的地址
int a = 20;
printf("%p", &a);// %p打印内存地址
int *p = &a;
  • 与普通变量不同的是,定义指针变量的时候,前面需要加上*

  • 使用*操作符访问内存编号里对应的内容

#include <stdio.h>

int main(int argc, const char * argv[])
{
    int a = 100;
    int *p = NULL;// NULL恒等于0
    p = &a;// 指针变量是存放地址的变量
    //printf("%d\n", *p);// *和&是配套的操作符
    //printf("%p\n%p\n%d", &a, p, *p);
    printf("%p\n%p\n%d\n%d\n", &a, p, *p, *&a);//0061FF28 0061FF28 100 100
    return 0;
}
  • 指针的重指向,指针变量的赋值意味着重指向。
#include <stdio.h>

int main(int argc, const char * argv[])
{
    int a = 100, b = 200;
    int *p = &a;
    p = &b;// 指针变量的赋值意味着重指向
    printf("%p\n%d\n",p,*p);// 0061FF24 200
    return 0;
}

指针变量初始化

  • 如果指针变量没有初始化,那么视图使用指针变量的值将会导致未定义的行为。
#include <stdio.h>

int main(int argc, const char * argv[])
{
    int *p;
    printf("%d\n", *p);

    return 0;
}

给指针变量赋值尤其危险

#include <stdio.h>

int main(int argc, const char * argv[])
{
    int *p;
    *p = 1;//如赋值改变的内存属于该程序则导致不规律的行为,若改变的内存属于操作系统则很可能导致系统崩溃。
    printf("%d\n", *p);

    return 0;
}

指针的类型

  • 因为内存地址只跟操作系统有关,所以指针变量所占内存大小取决于操作系统位数。即32位系统指针占4个字节,64位操作系统指针占8个字节。
  • 指针可分为 int *pidouble *pdchar *pc
#include <stdio.h>

int main(int argc, const char * argv[])
{
    int *pi = NULL;
    double *pd = NULL;
    char *pc = NULL;

    printf("%d %d %d", sizeof(int *), sizeof(double *), sizeof(char *));//4 4 4

    return 0;
}

注意事项

  • 指针变量定义 int *p=NULL;此时的*只起到修饰作用,告诉编译器,p是个指针。
  • p=&a; *p=20//此时的*是取内容操作符,将内存地址为p的存储区域中存入数据20。

指针赋值

C语言允许使用赋值运算符进行指针的赋值,前提是两个指针具有相同的数据类型。

#include <stdio.h>

int main(int argc, const char * argv[])
{
    int i, *p, *q;

    p = &i;
    q = p;// q和p存储都是i的地址
    printf("%d %d %d\n", i, *p, *q);// 0 0 0
    *p = 1;// 改变i的值为1
    printf("%d %d %d\n", i, *p, *q);// 1 1 1
    *q = 2;// 改变i的值为2
    printf("%d %d %d\n", i, *p, *q);// 2 2 2

    return 0;
}
#include <stdio.h>

int main(int argc, const char * argv[])
{
    int i, j, *p, *q;

    p = &i;//p指向i
    q = &j;//q指向j
    
    i = 1;
    *q = *p;//将p指向的值"复制"给q指向的值
    printf("%d %d %d %d\n", i, j, *p, *q);// 1 1 1 1

    return 0;
}

指针作为参数

  • 指针到底对什么有益呢?
  • 定义个交换两个整型变量的值的函数
#include <stdio.h>

void swap(int i, int j);

int main(int argc, const char * argv[])
{
    int i=1, j=2;
    swap(i,j);// 函数传参的过程,是把实际参数的值拷贝到形参中。
    printf("%d %d\n", i, j);// 1 2

    return 0;
}

void swap(int i, int j)
{
    int tmp = i;
    j = i;
    tmp = j;
}
#include <stdio.h>

void swap(int *i, int *j);

int main(int argc, const char * argv[])
{
    int i=1, j=2;
    swap(&i,&j);
    printf("%d %d\n", i, j);// 2 1

    return 0;
}

void swap(int *i, int *j)
{
    int tmp = *i;//将i变量中存储的地址中的内容赋值给tmp
    *i = *j;//将j变量存储地址上的值存储到i变量存储地址的值
    *j = tmp;
}

  • 由编译器自动分配和释放
  • 在函数体中定义的变量通常在栈内存空间上
  • 栈中的变量是先进后出
  • 栈中的变量一般出了函数就会被释放掉
#include <stdio.h>
int *fn(void);

int main(int argc, const char * argv[])
{
    int *pi = fn();
    printf("%d\n", *pi);// 1
    printf("%d\n", *pi);// 1521606922
    return 0;
}

int *fn(void)
{
    int num = 1;
    int *p = &num;
    return p;
}

静态区

  • 全局变量和静态变量的存储是放在一块的,存储在静态区。
  • 静态区中的变量在程序结束后立即被释放
#include <stdio.h>
int *fn(void);

int main(int argc, const char * argv[])
{
    int *pi = fn();
    printf("%d\n", *pi);// 1
    printf("%d\n", *pi);// 1
    return 0;
}

int *fn(void)
{
    static int num = 1; // 静态变量
    int *p = &num;
    return p;
}

小结:

  • 不仅可为函数传递指针还可编写返回指针的函数
  • 函数可以返回指向外部变量或指向声明为static的局部变量的指针
  • 永远不要返回指向自动局部变量的指针,因为函数结束后局部变量就不存在了,所以指向局部变量的指针是无效的。

结构体指针

  • 指向结构体变量的指针称为结构体指针
#include <stdio.h>

// 定义结构体
typedef struct {
    char name[20];
    int age;
    char sex;
} Student;

int main(int argc, const char * argv[])
{
    Student stu = {0};
    Student *p = &stu;// 定义结构体指针
    (*p).age = 10;//使用结构体指针访问成员
    printf("%d\n", p->age);// 10
    p->age = 20;
    printf("%d\n", p->age);// 20

    return 0;
}

结构体数组与指针的关系

#include <stdio.h>

// 定义结构体
typedef struct {
    char name[20];
    int age;
    char sex;
} Student;

int main(int argc, const char * argv[])
{
    Student stu[5] = {0};//定义结构体数组
    Student *p = stu;// 定义结构体指针
    p[0].age = 10;//使用结构体指针访问成员
    p[0].sex = 'm';
    printf("%d %c\n", p[0].age, p[0].sex);// 10 m

    return 0;
}

结构体内包含指针

#include <stdio.h>
#include <string.h>

// 定义结构体
typedef struct {
    char nickname[20];
    char *username;
} Student;

int main(int argc, const char * argv[])
{
    Student stu = {0};//定义结构体数组
    strcpy(stu.nickname, "alice");//数组名是一个常量指针,不可以改变其值。
    //warning: ISO C++ forbids converting a string constant to 'char*'
    stu.username = "bob";//字符指针是一个指针变量可重新赋值
    printf("%s %s\n", stu.nickname, stu.username);// alice bob

    return 0;
}

数组作为参数

  • 取一个数组中的最大元素
  • 有一个整型数组,将数组元素大于27的清零。
  • 数组作为参数,形参仅代表数组首元素地址,需传入元素个数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值