C语言指针(1)

简介: C语言指针(1)

指针(地址)

1. 指针是一个值为内存地址的变量。

2. 指针本质上是地址,是计算机存放数据的空间。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
  int a = 1;
  printf("%p", &a);
  return 0;
}
//%p用来打印地址(16进制形式打印)
//取出的是第一个字节的地址(较小的)
ptr=&pooh;
//ptr指向了pooh。
//ptr是变量,&pooh是常量。
//ptr的值是pooh的地址。

指针的声明

1. 指针使用之前需要声明。

int a;
int* pa=&a;
//int 说明指针指向变量的类型
//* 说明这是一个指针
//pa 说明了指针的名字

指针变量与解引用操作

1. 指针类型决定了在进行解引用操作时访问的空间大小。

2. 指针类型决定了指针的步长。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
  int a = 20;
  int* pa = &a;//创建指针变量pa来存放地址
  printf("%d", *pa);//*是解引用操作符,*pa==a
  return 0;
}

泛型指针

1. 可以接收任意类型的地址,但是不能进行加减运算和解引用操作。

2. 用于存放未知类型数据的地址。

3. 用于接收存储,不能对其进行操作。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
  int a = 20;
  void* pa = &a;
  return 0;
}

const 保护

const 修饰普通变量

1.  const表示常属性,普通变量被修饰后,就不能再被修改。

2.  普通变量被修饰后,可以通过对指针解引用的方式来修改。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
  const int a = 20;//a变成了常变量
  a = 10;//这一步报错,a的内容不能改变
  return 0;
}

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
  const int a = 20;
  int* pa = &a;
  *pa = 10;//通过解引用来修改常变量的值
  printf("%d", a);
  return 0;
}

const 修饰指针变量

const 如果放在 *  的左边:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
  const int a = 20;
    int b = 10;
  int const * pa = &a;
    //const放在*左边,修饰* pa,使得无法改变* pa(20),但可以改变指针指向的对象(&a改成&b)。
  *pa = 10;//报错
    pa = &b;//正确
  printf("%d", a);
  return 0;
}

const 如果放在 *  的右边:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
  const int a = 20;
    int b = 10;
  int* const pa = &a;
    //const放在*右边,修饰pa,使得无法改变pa(&a),但可以改变*pa(20)。
  *pa = 10;//正确
    pa = &b;//报错
  printf("%d", a);
  return 0;
}

指针运算

1. 指针与整数之间的运算,实际上要看指针指向的数据类型。

2. 指针指向的数据类型决定了指针的步长。

指针与整数运算  

#include <stdio.h>
//指针+-整数
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int* p = &arr[0];
    int i = 0;
    int sz = sizeof(arr)/sizeof(arr[0]);
    for(i=0; i<sz; i++)
    {
        printf("%d ", *(p+i));//(p+i)这⾥就是指针+整数,实际上是地址运算。
    }
    return 0;
}

 指针与指针比较                                                                            

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
//地址之间进行比较
int main()
{
  int arr1[10] = { 0 };
  int* p = arr1;
  int sz = sizeof(arr1) / sizeof(arr1[0]);
  while (p < sz + arr1)//地址之间进行比较
  {
    printf("%d", *p);
    p++;
  }
  return 0;
}

野指针  

1. 野指针就是:指向位置不可知的指针。

2. 尽量避免野指针的形成。

野指针成因

指针未初始化                                                                                                                    

#include <stdio.h>
int main()
{
  int* p;//局部变量指针未初始化,默认为随机值,即生成野指针,直接报错。
  *p = 20;
  return 0;
}


指针越界访问

#include <stdio.h>
int main()
{
  int arr[10] = { 0 };
  int* p = &arr[0];
  int i = 0;
  for (i = 0; i <= 11; i++)
  {
    //当指针指向的范围超出数组arr的范围时,p就是野指针,直接报错。
    *(p++) = i;
  }
  return 0;
}

指针指向的空间释放      

#include <stdio.h>
int* test()
{
  int n = 100;
  return &n;
}
//函数调用完后就把空间释放了。
int main()
{
  int* p = test();
  printf("%d\n", *p);
  return 0;
}

规避野指针

指针初始化

1. 如果明确知道指针指向哪⾥,就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL.

2. NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址会报错。

#include <stdio.h>//NULL是标准库里面的内容
int main()
{
  int num = 10;
  int* p1 = &num;
  int* p2 = NULL;
  return 0;
}

小心指针越界

1. ⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问。

避免返回局部变量的地址

1. 无法直接通过一个自定义函数来获得并声明一个指针。

2. 需要自己先声明一个指针,用NULL赋值,最后再通过传地址的方式,让该指针初始化。

#include <stdio.h>
int* test()
{
  int n = 100;
  return &n;
}
//函数调用完后就把空间释放了。
int main()
{
  int* p = test();
  printf("%d\n", *p);
  return 0;
}


assert 断言

1. assert()⽤来判断程序是否符合指定条件,如果不符合,就报错终⽌运⾏。


2. 需要引入<assert.h>头文件。


3. 如果已经确认程序没有问题,就不需要再做断⾔,可以在 #include 语句的前⾯,定义⼀个宏 NDEBUG ,那么assert()将不会发挥作用。


#define _CRT_SECURE_NO_WARNINGS
#include <assert.h>
int main()
{
  int a = 20;
  int* pa = &a;
  int* pb = NULL;
  assert(pb != NULL);//报错,退出程序
}
#define NDEBUG
#include <assert.h>

传值调用和传址调用

传值调用

1. 实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实参。

#include <stdio.h>
void Swap1(int x, int y)
{
  int tmp = x;
  x = y;
  y = tmp;
}
int main()
{
  int a = 0;
  int b = 0;
  scanf("%d %d", &a, &b);
  printf("交换前:a=%d b=%d\n", a, b);
  Swap1(a, b);
  printf("交换后:a=%d b=%d\n", a, b);
  return 0;
}
//没有实现效果

传址调用

1. 将main函数中将a和b的地址传递给Swap函数,Swap 函数里边通过地址,间接操作main函数中的a和b,达到交换的效果。

#include <stdio.h>
void Swap2(int* px, int* py)
{
  int tmp = 0;
  tmp = *px;
  *px = *py;
  *py = tmp;
}
int main()
{
  int a = 0;
  int b = 0;
  scanf("%d %d", &a, &b);
  printf("交换前:a=%d b=%d\n", a, b);
  Swap2(&a, &b);
  printf("交换后:a=%d b=%d\n", a, b);
  return 0;
}


总结

1. 传址调用,可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量。

2. 所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采⽤传值调用;

3. 如果函数内部要修改主调函数中的变量的值,就需要传址调⽤。


致谢

 感谢您花时间阅读这篇文章!如果您对本文有任何疑问、建议或是想要分享您的看法,请不要犹豫,在评论区留下您的宝贵意见。每一次互动都是我前进的动力,您的支持是我最大的鼓励。期待与您的交流,让我们共同成长,探索技术世界的无限可能!

相关文章
|
10天前
|
存储 C语言
【C语言篇】深入理解指针3(附转移表源码)
【C语言篇】深入理解指针3(附转移表源码)
24 1
|
10天前
|
存储 程序员 编译器
【C语言】指针篇-简单快速了解指针-必读指南(1/5)
【C语言】指针篇-简单快速了解指针-必读指南(1/5)
|
2天前
|
存储 搜索推荐 C语言
深入C语言指针,使代码更加灵活(二)
深入C语言指针,使代码更加灵活(二)
|
2天前
|
存储 程序员 编译器
深入C语言指针,使代码更加灵活(一)
深入C语言指针,使代码更加灵活(一)
|
2天前
|
C语言
深入C语言指针,使代码更加灵活(三)
深入C语言指针,使代码更加灵活(三)
深入C语言指针,使代码更加灵活(三)
|
8天前
|
存储 C语言
深入浅出C语言指针(基础篇)
深入浅出C语言指针(基础篇)
|
10天前
|
算法 搜索推荐 C语言
【C语言篇】深入理解指针4(模拟实现qsort函数)
【C语言篇】深入理解指针4(模拟实现qsort函数)
17 2
|
10天前
|
C语言 C++
【C语言】指针篇-一篇搞定不同类型指针变量-必读指南(3/5)
【C语言】指针篇-一篇搞定不同类型指针变量-必读指南(3/5)
|
1月前
|
存储 C语言
【C语言基础】一篇文章搞懂指针的基本使用
本文介绍了指针的概念及其在编程中的应用。指针本质上是内存地址,通过指针变量存储并间接访问内存中的值。定义指针变量的基本格式为 `基类型 *指针变量名`。取地址操作符`&`用于获取变量地址,取值操作符`*`用于获取地址对应的数据。指针的应用场景包括传递变量地址以实现在函数间修改值,以及通过对指针进行偏移来访问数组元素等。此外,还介绍了如何使用`malloc`动态申请堆内存,并需手动释放。
|
8天前
|
存储 C语言 C++
深入浅出C语言指针(进阶篇)
深入浅出C语言指针(进阶篇)