美文网首页
09-C语言指针

09-C语言指针

作者: 低头看云 | 来源:发表于2018-09-16 17:10 被阅读0次

什么是指针

  • 指针

    • 存放地址的是指针
  • 指针变量

    • 指针是存放指针的变量
    • 指针变量和普通变量一样,都可以用来保存数据,当做形参,当做返回值;
    • 只不过指针变量只能保存内存地址
    • 简而言之,指针变量,就是专门用于保存内存地址的变量
  • 定义指针变量的格式

int num = 6;  // 定义一个普通变量

int *p;  // 定义一个指针变量
// num变量的占用内存最小的那个地址存储到指针变量p中
p = #

  • p是代表取出p中存储的值
  • &p是代表取出各自的内存地址
int num = 6;
int *p;
p = #   // p指向num地址
printf("&num = %p\n", &num);   // 0028FEBC
// 代表取出p中存储的值
printf("p = %p\n", p);         // 0028FEBC
// &p和普通变量也一样,代表取出各自的内存地址
printf("&p = %p\n", &p);       // 0028FEB8

指针变量的初始化方法

  • 指针变量初始化的方法有两种:定义的同时进行初始化和先定义后初始化
  • 定义的同时进行初始化
int a = 5;
int *p = &a;
  • 先定义后初始化
int num = 6;
int *p;
p = #
- - 把指针初始化为NULL
```
int *p = NULL;
int *p = 0;
```
  • 给指针变量赋值时,指针变量前不要再加 "*" 星号;
int num = 6;
int *p;
*p = # // 错误写法

指针的大小和指向问题

  • 指标变量占用的内存大小都一样,在32位编译器中,占用4个字节;在64位编译器中,占用8个字节
  • 只要一个变量存储了另一个变量的内存地址,那么我们就说这个变量指向了另外一个变量
  • 只要一个变量存储另外一个变量的内存地址,我们就可以根据这个内存地址找到另外一个变量的内存,从而操作那一块内存
```
int num = 6;
int *p;
p = #
printf("修改前: num = %i\n", num); // 6
*p = 88;
printf("修改后: num = %i\n", num); // 88
printf("修改后: *p = %i\n", *p);   // 88
```
  • 指针的注意点:
    • 1.指针只能保存地址,不能保存其它的值
    int *p = 1.1; // 注意千万不要这样写
    
    • 2.一个变量可以被多个指针指向
    int num = 6;
    int *p1;
    int *p2;
    p1 = #
    p2 = #
    printf("*p1 = %i\n", *p1);  // 6
    printf("*p2 = %i\n", *p2);  // 6
    
    • image
    • 3.指针的指向可以发生改变

    int num = 6;
    int *p = #  // p ---> num
    int value = 555;
    p = &value;   // p ---> value
    printf("%i\n", *p);
    
    • 4.定义指针变量时前面的数据类型,必须和将来要保存的变量前面的数据类型一致;

指针为什么要有类型

  • 由于指针变量指向的是某一个变量占用存储空间的首地址
  • 所以在获取指向变量中存储数据的时候,指针是不知道要获取多少个字节的数据
  • 所以指针变量的作用:就是告诉指针,在获取指向变量的数据的时候,需要获取几个字节的数据
int num = 888;
//0000 0000 0000 0000 0010 0010 1011 1000
//int *p;
char *p;
p = #
printf("*p = %i\n", *p); // 888    120(改成char类型会发生数据错误)
image

小例子

  • 定义一个函数, 要求能够在函数中修改传入变量的值
#include <stdio.h>
// void change(int value);
int main()
{
    int num = 6;
    printf("调用前: num = %i\n", num);   // 6     6
    // change(num);
    change(&num);
    printf("调用后: num = %i\n", num);   // 6     666
    return 0;
}

//void change(int value){
void change(int *value){
    *value = 666;
}

  • 基本类型

    • 基本类型作为形参,在函数内修改形参,不会影响到函数外的实参;
  • image
  • 指针类型

  • image
  • 要定定义一个函数, 在函数中交换传入变量的值
#include <stdio.h>
void swap(int *num1, int *num2);
int main()
{
    // 需求: 要定定义一个函数, 在函数中交换传入变量的值
    int a = 4;
    int b = 6;
    printf("a = %i, b = %i\n" ,a, b);  // 4   6
    swap(&a, &b);
    printf("a = %i, b = %i\n", a, b);  // 6   4
    return 0;
}

// 传入的地址,则会修改实参
void swap(int *num1, int *num2){
    int temp = *num1;
    *num1 = *num2;
    *num2 = temp;
}

image
  • 定义一个函数,可以同时返回两个变量的和, 差, 积, 商
#include <stdio.h>
void test(int a, int b, int *c, int *d,int *e,int *f);
int main()
{
    //要求定义一个函数,可以同时返回两个变量的和, 差, 积, 商
    int a = 10;
    int b = 20;

    int c, d, e, f;
    test(a, b, &c, &d, &e, &f);
    printf("和 = %i\n", c);
    printf("差 = %i\n", d);
    printf("积 = %i\n", e);
    printf("商 = %i\n", f);

    return 0;
}

// 注意点:return的作用是用于结束当前函数,return后面的代码执行不到;
// 可以通过操作指针,实现地址的传递;来实现多个返回值;
void test(int a, int b, int *c, int *d,int *e,int *f){
    *c = a + b;
    *d = a - b;
    *e = a * b;
    *f = a / b;
}

二级指针

  • 二级指针,就是指向指针的指针

  • 定义指针的规律

    • 1.将要指向变量的定义拷贝过来
    • 2.在拷贝过来的代码的类型和变量名称中间加上一颗星 *;
    • 3.修改变量名称
int num;
num = 6;

// 一级指针
int *p;
p = &num;

// 二级指针
int **pp;
pp = &p;

  • 访问二级指针
int num;
num = 6;

// 一级指针
int *p;
p = &num;
printf("p
= %i\n", *p);

// 二级指针
int **pp;
pp = &p;
printf("pp = %i\n", **pp);   // 6

image
  • 什么是多级指针

    • 多级指针就是指针中保存的有事其它指针的地址,我们就称为多级指针
  • 指针访问规律: 如果想通过多级指针获取某个变量的值,那么是几级指针,前面就写几颗星即可;

指针和数组

  • 数组名称保存的就是数组占用内存最小的那个地址
  • 既然数组名称保存就是地址,而指针也是用于保存地址,所以指针也可以指向数据
int ages[3] = {1, 3, 5};
int *p = &ages;   // p = ages = &ages;
  • 结论:如果利用指针保存数组的地址之后,那么p = ages = &ages;
  • 指针遍历数组
#include <stdio.h>

int main()
{
    int ages[3] = {1, 3, 5};
    for(int i = 0; i< 3; i++){
        printf("ages[%i] = %i\n", i, ages[i]);
    }
    printf("------------------------\n");
    int *p = &ages;
    for(int i = 0; i < 3; i++){
        //printf("agee[%i] = %i\n", i , *p++);
        printf("agee[%i] = %i\n", i , p[i]);
    }

    return 0;
}
  • 操作数组
int ages[3] = {1, 3, 5];
int *p = &ages;
// ages[0] = 8;
p[0] = 88;
  • 访问数组的三种写法
int ages[3] = {1, 3, 5};
// 1.第一种
printf("ages[0] = %i\n", ages[0]);  //   1
// 2.第二种
int *p = ages;
printf("p[0] = %i\n", p[0]);   //    1
// 3.第三种
printf("0[p] = %i\n", 0[p]);   //   1
  • 指针可以进行加减法
  • 指针 +1, -1;
  • 应用场景: 一般都是用于指针数组中
int nums[3] = {1, 3, 5};
int *p = &nums;  // 等同于   int *p = nums;
printf("*p = %i\n", *p);  // 1;
printf("*(p + 1) = %i\n", *(p + 1)); //   3   
printf("*(p + 2) = %i\n", *(p + 2)); //   5
printf("*p = %i\n", *p);  // 1;
// 此时p还是执行nums[0]的地址

printf("*p = %i\n", *p++);  // 1;
printf("*p = %i\n", *p++);  // 3;
printf("*p = %i\n", *p);  // 5;
printf("*p = %i\n", *(--p));  // 3
// 指针先++,再取出里面的内容;
  • 注意点: 指针变量 +1是加多少, 加的就是指针变量类占用的字节数;
  • 如果指针的类型是int类型, 那么 +1 相当于 +4 个字节

指针和字符串

  • 字符串的本质就是数组,所以指针也可以指向字符串
char str1[] = {'c','w','w','\0'};
char str2[10] = {'c','w','w'};
char str3[] = "cww";
//    printf("str1 = %s\n",str1);   // cww
//    printf("str2 = %s\n",str2);   // cww
//    printf("str3 = %s\n",str3);   // cww

// 注意下面写法是错误的
char *str4 = {'c','w','w','\0'};
printf("str4 = %s\n",str4);   // 罢工
  • 利用数组和指针定义字符串的区别

    • 1.存储的位置不同
      • 如果是通过数组定义的字符串,那么存储在内存的栈中
      • 如果是通过指针定义的字符串,那么存储在内存的常量区中
    • 2.由于在内存中存储的位置不一样
      • 如果通过数组定义的字符串,我们是可以手动修改的
      • 但是通过指针定义的字符串,我们是不能手动修改的
      char str[] = "cww";
      char *str2 = "cww";
      str[1] = 'T';
      //printf("str = %s\n", str);  // cTw
      //str2[1] = 'T';
      printf("str2 = %s\n", str2);   // 罢工
      
      
    • 3.由于内存中的存储位置不一样
      • 如果是通过数组定义的字符串,每次定义都会重新开辟存储空间;
      • 如果是通过指针定义的字符串,重复定义不会重新开辟存储空间;
     // 存储地址不一样
    char str1[] = "cww";
    char str2[] = "cww";
    printf("str1 = %p\n",str1);  // 0028FEB4
    printf("str2 = %p\n",str2);  // ))28FEB0
    
    // 存储地址一样
    char *str3 = "cww";
    char *str4 = "cww";
    printf("str3 = %p\n",str3);   // 0040407A
    printf("str4 = %p\n",str4);   // 0040407A  
    
  • 注意点:

    • 接收字符串的时候,只能通过字符数组,不能通过指针
    char str1[10];
    char *str2;
    scanf("%s",str1); // 可以接收
    scanf("%s",str2);  // 不可以接收
    printf("str = %s\n",str1);
    
    • 将过去形参的数组类型修改为指针类型
    void test(int str1[]){
        被执行语句;
    }
    // 修改为
    void test(int *str1){
        被执行语句;
    }
    
    • 如果函数中返回的字符串是通过数组创建的,那么外界无法获取
    #include <stdio.h>
    char* test();
    int main()
    {
    
    
        char* res = test();
        printf("res = %s\n", res);  // 无法获取
    
        return 0;
    }
    
    char* test(){
        char str[] = "cww";
        return str;
    }
    
    
    • 如果函数中返回的字符串是通过指针创建的,那么外界可以获取
    #include <stdio.h>
    char* demo();
    int main()
    {
    
    
        char* res = demo();
        printf("res = %s\n",res); // 可以获取
    
        return 0;
    }
    
    char* demo(){
        char *str = "cww";
        return str;
    }
    
    
    image

指向函数指针

  • 为什么指针可以指向一个函数
    • 函数作为一段程序,计算机也会给函数分配存储空间,既然函数会分配内存空间,
    • 所以函数也有自己的地址,所以指针变量也可以保存函数的地址
- 数组名保存的就是数组的地址,同样,**函数名中保存的就是函数的地址**
    - 如果是数组,我们可以直接将数组名称赋值给一个指针变量
    - 如果是函数,我们也可以直接将函数名称赋值给一个指针变量
   
```
#include <stdio.h>
void test();
int main()
{
    test();
    printf("test = %p\n", test);  //  00401672
    printf("&test = %p\n", &test);  //  00401672
    return 0;
}

void test(){
    printf("test\n");
}

``` 
  • 如何定义保存函数的指针变量

    • 1.将函数的声明拷贝过来
    • 2.在函数返回值和函数名称中间添加一个 * 星
    • 3.修改函数名称
    • 4.注意:需要将星号和变量名称用括号括起来
    #include <stdio.h>
    void test();
    int main()
    {
    
        void (*funcP)(); // 指向函数的指针
        // funcP = &test;  // 将函数的地址赋给指针
        // 等同于
        funcP = test;
        return 0;
    }
    // test函数
    void test(){
        printf("test\n");
    }
    
  • 如果一个指针指向了数组,那么访问数组有三种方式

    • 1.数组名称[索引];
    • 2.指针变量名称[索引];
    • 3.*(指针编码名称 + 索引);
    int nums[] = {1, 3, 5};
    int *p = &nums;
    // 数组名称[索引]; 
    printf("nums[1] = %i\n", nums[1]);
    
    // 指针变量名称[索引];
    printf("nums[1] = %i\n", p[1]);
    
    // *(指针编码名称 + 索引);
    printf("nums[1] = %i\n", *(p +1));
    
  • 如果一个指针指向了函数,那么也有多种访问方式

    • 1.函数名称();
    • 2.(*指针变量名称)();
    • 3.指针变量名称();
    #include <stdio.h>
    void test();
    int main()
    {
    
        // 第一种:函数名称(); 
        test();   
        
        void (*funcP)(); // 指向函数的指针
        funcP = &test;
        // 等同于
        // funcP = test;
        
        // 第二种:(*指针变量名称)();
        (*funcP)();   
        
        // 第三种:指针变量名称();
        funcP();     
        return 0;
    }
    
    void test(){
        printf("test\n");
    }
    
    
  • 小练习
#include <stdio.h>
void say();
void sum(int a, int b);
int minus(int a, int b);
char* test();
int main()
{
    // 指针指向函数的练习

    // 1.
//    void (*p1)();
//    p1 = say;
//    p1();


    // 2.
//    void (*p2)(int , int);
//    p2 = sum;
//    p2(10, 20);


    // 3.
//    int (*p3)(int, int);
//    p3 = minus;
//    int res = p3(30,20);
//    printf("res = %i\n", res);


    // 4.
    char* (*p4)();
    p4 = test;
    char* res = p4();
    printf("res = %s\n", res);

    return 0;
}


void say(){
    printf("Hello World!\n");
}

void sum(int a, int b){
    printf("sum = %i\n",a + b);
}

int minus(int a, int b){
    return a-b;
}


char* test(){
    char *name = "cww";
    return name;
}


#include <stdio.h>
int sum(int a , int b);
int minus(int a, int b);
int test(int num1, int num2, int (*func)(int ,int));
int main()
{

    int (*p1)(int ,int);
    p1 = sum;
    // p1 = &sum;  // 二者写法一致;
    int res1 = p1(10, 20);
    printf("res1 = %i\n", res1);   // 30

    p1 = &minus;
    int res2 = p1(10, 20);
    printf("res2 = %i\n", res2);   // -10

    int res3 = test(10, 20, sum);
    printf("res3 = %i\n", res3);   // 30

    // int res4 = test(10, 20, &minus);
    int res4 = test(10, 20, minus);
    printf("res4 = %i\n", res4);    // -10

    return 0;
}

/*
    指向函数的指针,作为函数的形参时,指针变量的名称, 就是形参的名称
    如果指向函数的指针作为函数的参数,那么这个可以称之为回调函数;
    这里相当于,给test函数传入了一个sum或minus函数
    然后又在test函数中调用了一个sum函数或者是minus函数
*/
int test(int num1, int num2, int (*func)(int ,int)){
    return func(num1, num2);  
    // return (*func)(num1,num2);
}

int sum(int a , int b){
    return a + b;
}

int minus(int a, int b){
    return a -b;
}

image
  • 指向函数的指针,作为函数的形参时,指针变量的名称, 就是形参的名称
    如果指向函数的指针作为函数的参数,那么这个可以称之为回调函数;
    这里相当于,给test函数传入了一个sum或minus函数
    然后又在test函数中调用了一个sum函数或者是minus函数
image

相关文章

  • 09-C语言指针

    什么是指针 指针存放地址的是指针 指针变量指针是存放指针的变量指针变量和普通变量一样,都可以用来保存数据,当做形参...

  • 09-C语言函数

    函数基本概念 C源程序是由函数组成的例如: 我们前面学习的课程当中,通过main函数+scanf函数+printf...

  • C语言05- 指针

    C语言05- 指针 13:指针 指针是C语言中的精华,也是C语言程序的重点和难点。 13.1:指针定义与使用 指针...

  • 02-C语言的指针

    02-C语言的指针 目标 C语言指针释义 指针用法 指针与数组 指针与函数的参数 二级指针 函数指针 指针在C中很...

  • 带小白学C语言指针

    C语言里指针才是C语言的开始和指针;C语言里基本所有东西都是由指针演变而成; 指针是指向地址的变量,类型就是指针...

  • Go语言-指针

    Go语言中的指针不同于C语言,Go语言的指针使用方法要简单很多。当然和C语言从指针定义到指针的使用都有很大的不同。...

  • go语言值传递与指针传递

    go语言指针 go语言作为静态编译型语言,具有指针类型,但是不提供对指针的移位危险操作,防止指针的越界等问题。 g...

  • Go语言 指针

    指针 概念 Go语言的指针和C语言一样也是用来保存地址的变量 格式 普通指针 指向数组的指针 注意点 GO语言中只...

  • 2018-06-05

    C语言指针教程 (一)什么是C语言指针指针是C语言中广泛使用的一种数据类型。运用指针编程是C语言最主要的风格之一。...

  • C语言指针总结大学霸IT达人

    C语言指针总结大学霸IT达人 C语言的指针是C语言区别其它语言的最主要的特定之一。有了指针,C语言就可以抛开所有束...

网友评论

      本文标题:09-C语言指针

      本文链接:https://www.haomeiwen.com/subject/nurfnftx.html