C语言学习笔记

本文详细介绍了C语言中数组、指针和函数的使用。涵盖了数组的基本形式、初始化、遍历及字符串操作;指针的概念、类型、运算以及在二维数组中的应用;函数的声明、定义、参数传递方式和回调函数的运用。同时提供多个实践练习以加深理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C语言3大难点:数组、指针、函数

数组

多个同种数据类型数据的集合,在内存空间上连续存储

基本形式:

<存储类型> <数据类型> <数组名>[元素个数]

存储类型:数组存储的方式

数据类型:存储的多个元素是什么数据类型,就写什么数据类型

数组名:和标识符的命名规则保持一致

是数组的首个元素的地址,代表了整个数组

元素个数:数组想要存放多少个元素,这里就写多少;写的是一个常量(普通整型常量:10,20,5;标识符常量:#denfine SIZE 20)

声明和定义:

int arr[5];
char buf[10];

初始化:

1、完全初始化

数组的每一个元素,都赋予一个初始值
例:
int arr[5] = {0,1,2,3,4};
char buf[5] = {'h','e','l','l','o'};

2、部分初始化

数组的前面部分元素给予一个初值
例:
int arr[5] = {0,1,2};
char buf[5] = {'w','o'};
未被赋值的元素默认为0(字符数组默认为'\0')
​
int arr[5] = {0};//全部为0
char buf[5] = {0};//全部为'\0'

3、未初始化

定义时未给元素赋初值
例:
int arr[5];
char buf[5];
数组中的元素为随机值

4、省略初始化

定义时未给定元素个数
例:
int a[] = {1,2,3};
初始化时给定多少个元素,系统就分配多大的空间

数组的使用:

通过数组名加下标的方式去访问

int a[5];
​
该数组能访问到的元素:a[0]     a[1]    a[2]    a[3]    a[4]
若一个数组的元素个数为n
那该数组能访问到的下标为0~n-1
​
若访问a[5]则数组越界访问(非法访问内存),会产生段错误
​
​
定义数组时,下标表示元素个数
在使用数组时,下标表示的是第几个元素
注意:定义和使用时,下标的含义不一样

数组的遍历

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    int arr[5] = {0, 1, 2, 3, 4};
    char buf[10];
​
    int i = 0;
    for( i = 0; i < 5; i++)
    {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
   
    return 0;
} 
​
结果:
arr[0] = 0
arr[1] = 1
arr[2] = 2
arr[3] = 3
arr[4] = 4
​

练习:

定义一个整型数组,计算所有元素的和,并打印其中的最大值和最小值及其下标

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    int a[10] = {7,81,3,99,102,24,17,1,82,56};
​
    int sum = 0;
    int i = 0;
    int max = a[0];
    int min = a[0];
    int max_i = 0;
    int min_i = 0;
    for( i = 0; i < 10; i++)
    {
        if( a[i] >= max)
        {
            max = a[i];
            max_i = i;
        }
        if( a[i] <= min)
        {
            min = a[i];
            min_i = i;
        }
        sum += a[i];
    }
    printf("sum:%d\n", sum);
    printf("max:%d  max_i:%d\n", max, max_i);
    printf("min:%d  min_i:%d\n", min, min_i);
    return 0;
} 
​
结果:
sum:472
max:102  max_i:4
min:1  min_i:7
​

字符数组和字符串

字符数组可以用来存放字符串

char buf[6] = {'h','e','l','l','o'};
char buf[6] = "hello";
​
char buf[5] = {'h','e','l','l','o'};
char buf[5] = "hello"//数组越界访问
​
注意:用字符数组存放字符串时,一定要给'\0'预留空间

练习:从键盘上输入一个字符串,求该字符串的有效长度

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    char buf[10] = {0};
    gets(buf);
    int i = 0;
    while(buf[i] != '\0')
    {
        i++;
    }
    /*
    for(i = 0; i < 10; i++)
    {
        if( buf[i] == '\0')
        {
            printf("i:%d\n", i);
            break;
        }
    }
    */
    printf("i:%d\n", i);
    return 0;
} 

练习:从键盘上获取两个字符串,分别存放在两个数组里面,比较两个数组是否相同,若相同,输出0,若第一个比第二个大,则输出1,如果第一个比第二个小,则输出-1;

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    char a[10] = {0};
    char b[10] = {0};
    gets(a);
    gets(b);
    int i = 0;
    for( i = 0; i < 10; i++)
    {
        if( a[i] > b[i])
        {
            printf("1\n");
            break;
        }
        else if( a[i] < b[i])
        {
            printf("-1\n");
            break;
        }
        else
        {
            if( 9 == i)
                printf("0\n");
            continue;
        }
    }
    return 0;
}

数组的大小

sizeof(数组名):计算数组的字节大小
​
int arr[10] = {0};
sizeof(arr) == 40;(4*10)即   数据类型所占字节数*数组的元素个数
​
sizeof(数组名)/sizeof(数组的数据类型) -->元素个数
sizeof(数组名)/元素个数 -->数组的数据类型

字符串函数

1、strlen

头文件:#include    <string.h>
函数原型:size_t strlen(const char *s);
作用:求字符串有效长度
例:

#include <stdio.h>
#include <string.h>
​
int main(int argc, char *argv[])
{ 
    char buf[10] = {0};
    int arr[10] = {0};
    scanf("%s", buf);
    printf("sizeof(buf):%ld\n", sizeof(buf));
    printf("strlen:%ld\n", strlen(buf));
    printf("siezof(arr):%ld\n", sizeof(arr));
    return 0;
}
 
​
结果:
asdf
sizeof(buf):10
strlen:4
siezof(arr):40
​

2、strcmp

头文件:#include <string.h>
函数原型:int strcmp(const char *s1, const char *s2);
返回值:
如果两个字符串相等,返回值为0
如果前一个字符串比后一个大,返回值为不同的那两个字符之间的差值,并且为正数
如果前一个字符串比后一个小,返回值为不同的那两个字符之间的差值,并且为负数
​
比较是一个一个字符从头开始比较,若相同则比较下一个字符,直到比较完所有的字符
​
例:

#include <stdio.h>
#include <string.h>
​
int main(int argc, char *argv[])
{ 
    char a[10] = {0};
    char b[10] = {0};
    gets(a);
    gets(b);
    printf("strcmp:%d\n", strcmp(a,b));
    return 0;
} 
​
结果:
asd
asd
strcmp:0
 
aad
asd
strcmp:-18
​
aad
asd
strcmp:-18
 
abd
asd
strcmp:-17
 
abd
acd
strcmp:-1
 
acd
abd
strcmp:1
​
asd
aad
strcmp:18
​

3、strcat

头文件:#include <string.h>
函数原型:char *strcat(char *dest, const char *src);
​
作用将后面一个字符串,粘贴到前面一个字符串的后头
​
例:

#include <stdio.h>
#include <string.h>
​
int main(int argc, char *argv[])
{ 
    char a[10] = {0};
    char b[10] = {0};
    gets(a);
    gets(b);
    strcat(a,b);
    puts(a);
    puts(b);
    return 0;
} 
​
结果:
asd
bnm
asdbnm
bnm
​

4、strcpy

头文件:#include <string.h>
函数原型:char *strcpy(char *dest, const char *src);
​
例:

#include <stdio.h>
#include <string.h>
​
int main(int argc, char *argv[])
{ 
    char a[10] = {0};
    char b[10] = {0};
    gets(a);
    gets(b);
    strcpy(a,b);
    puts(a);
    puts(b);
    printf("a[4]:%c\n", a[4]);
    return 0;
} 
​
结果:
asdzxcv
bnm
bnm
bnm
a[4]:x
​
结论:该函数并不是完完全全的复制,而是将后一个字符串复制一份出来压到前一个字符串里面

作业:不用字符串函数,实现strcat和strcpy

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    char a[10] = {0};
    char b[10] = {0};
    gets(a);
    gets(b);
    int i = 0;
    for(i = 0; i < 10; i++)
    {
        a[i] = b[i];
        if( b[i] == '\0')
        {
            break;
        }
    }
    puts(a);
    puts(b);
    return 0;
} 
​

#include <stdio.h>
#include <string.h>
​
int main(int argc, char *argv[])
{ 
    char a[10] = {0};
    char b[10] = {0};
    gets(a);
    gets(b);
    int len = strlen(a);
    for(int i = 0; i < 10;i++)
    {
        a[len] = b[i];
        len++;
        if( b[i] == '\0')
            break;
    }
    puts(a);
    puts(b);
    return 0;
} 

从键盘输入一个数, 计算这个数 补码 中 1 的个数

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    unsigned int a = 0;
    int temp = 0;
    scanf("%d", &a);
    while( a != 0)
    {
        if( a % 2 == 1)
            temp++;
        a /= 2;
    }
    printf("temp:%d\n", temp);
    return 0;
} 

利用 * 打印一个菱形

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    int n = 0;
    scanf("%d", &n);
    int i,j;
    for( i = 0; i < n/2+1;i++)
    {
        for(j = 0;j < n/2 - i;j++)
        {
            printf(" ");
        }
        for(j = 0;j < 2*i + 1;j++)
        {
            printf("*");
        }
        putchar('\n');
    }
    for( i = 0; i < n/2;i++)
    {
        for(j=0;j<=i;j++)
        {
            printf(" ");
        }
        for(j=0;j<n-2-2*i;j++)
        {
            printf("*");
        }
        putchar('\n');
    }
    return 0;
} 

冒泡排序

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    int i = 0;
    int j = 0;
    int a[10] = { 3, 7, 76, 98, 4, 23, 54, 67, 31, 2};
    int temp = 0;
    for(j = 0; j < 10; j++)
    {
        for( i = 0; i < 9; i++)
        {
            if( a[i] > a[i+1])
            {
                temp = a[i];
                a[i] = a[i+1];
                a[i+1] = temp;
            }
        }
    }
​
    for( i = 0; i < 10; i++)
    {
        printf("a[%d]:%d  ",i, a[i]);
    }
    putchar('\n');
    return 0;
} 

二维数组

多个同种类型一维数组的集合

一般形式:

<存储类型>  <数据类型>  <数组名>[下标][下标]
<存储类型>  <数据类型>  <数组名>[行数][列数]
​
例:
int a[2][3];
​
数组名:
a:是这个二维数组首行的地址,代表了整个二维数组
a[0]:是二维数组中首个一维数组的首元素地址,代表了首个一维数组

定义和声明

int  a[2][3];
char b[2][3];

数组元素的使用

数组名[下标][下标]
​
如果数组的第一个下标为n,第二个下标为m
最多能访问到a[n-1][m-1];
最小为a[0][0];

二维数组的初始化

1、完全初始化

include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    int a[2][3] = {{1,2,3},{4,5,6}};
    int b[2][3] = {1,2,3,4,5,6};
    char c[2][3] = {{"he"},{"wa"}};
    char buf[2][3] = {"he","wa"};
    //char d[2][3] = {"hello"};
    printf("%c\n", buf[0][0]);
    return 0;
} 

2、部分初始化

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    int a[2][3] = {{1,2},{3}};
    int b[2][3] = {1,2,3};
    char c[2][3] = {'h','e','l'};
    char d[2][3] = {"h","el"};
​
    return 0;
} 
​

3、未初始化

数组中的元素为随机值

4、省略初始化

第一个下标可以省略(即行号可以省略,列号不行)
int a[][3] = {1,2,3,4};

二维数组的遍历

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    int a[2][3];
    int i = 0;
    int j = 0;
    for(j = 0; j < 2;j++)
    {
        for( i = 0; i < 3; i++)
        {
            printf("a[%d][%d]:%d\n", j, i, a[j][i]);
        }
    }
    return 0;
} 

练习:

定义一个二维数组,往里面放五个国家的名字,然后进行一个从小到大的排序;

#include <stdio.h>
#include <string.h>
​
int main(int argc, char *argv[])
{ 
    char buf[5][10] ={"China","German","Franch","Japan","England"};
​
    /*
       char a[10] = "asd";
       char b[10] = "bnc";
       char c[10] = {0};
       int i = 0;
       for(i = 0; i < 10; i++)
       {
       c[i] = a[i];
       a[i] = b[i];
       b[i] = c[i];
       }
       puts(a);
       puts(b);
       */
    char c[10] = {0};
    int i = 0;
    int j = 0;
    for(j = 0; j < 4; j++)
    {
        for( i = 0;i < 4; i++)
        {
            if(0 < strcmp(buf[i],buf[i+1]))
            {
                strcpy(c,buf[i]);
                strcpy(buf[i],buf[i+1]);
                strcpy(buf[i+1],c);
            }
        }
    }
    for(i = 0; i < 5; i++)
    {
        printf("%s\n", buf[i]);
    }
    return 0;
} 
 

练习:打印杨辉三角

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    int a[30][30] = {0};
    int i = 0;
    int j = 0;
    int n = 0;
    scanf("%d", &n);
​
    for( j = 0; j < n; j++)
    {
        for( i = 0; i < j + 1;i++)
        {
            if( i == 0 || i == j)
            {
                a[j][i] = 1;
            }
            else
            {
                a[j][i] = a[j-1][i] + a[j-1][i-1];
            }
        }
    }
​
    for( j = 0; j < n; j++)
    {
        for( i = 0; i < j + 1;i++)
        {
            printf("%d  ", a[j][i]);
        }
        putchar('\n');
    }
    return 0;
} 

指针

指针就是地址,地址就是内存空间中每一个内存单元(1byte(字节))的“门牌号”

如 内存空间中第2个内存单元的地址(门牌号)就是0x2

指针变量

基本形式:

<数据类型>  <*> <指针名>;
例:
int *p;
char *q;

作用:

用于保存地址(指针)

初始化:

例:
int a = 10;
char b = 'A';
​
int *p = &a;
​
如果初始化时没有需要保存的地址,则赋值为空;
例:
int *p = NULL;((void *)0)
​
int *p;//野指针
p = &a;
*p = 10;
​
int *p,m =10,n;
int *p;
int m =10;
int n;
​
​

指针的使用:

&:后面跟变量名,表示取该变量的地址
*:  
使用时,表示取该地址所对应的内存空间中的内容
定义变量时,表示该变量是一个指针变量
​
*和&符号互为逆运算
​
int a = 10;
int *p = &a;
*p == *(&a) ==  a

指针的运算:

 int a = 10;
 int b =20;
 int *p = &a;
 int *q = &b;
 指针的基础运算
 加法:
 p + 3;
 p++;
 p - 3;
 p--;
 
 p + q -->无任何含义的
 p - q -->计算地址的高低,两个指针之间有多少个元素
 * / %无实际意义
 
 p + n  --> 指针偏移
 p + n*sizeof(指向类型)

指针类型和指向类型:

指针类型:去掉指针变量的变量名之后,剩余的部分就叫指针类型
例:
int *p = NULL;
它的指针类型就是:int *类型
​
指向类型:去掉指针变量的变量名之后,再去掉与其相邻的第一个*,剩余的部分就叫指向类型
int *p = NULL;
它的指向类型就是:int类型
​
int **p;
int (*)p[6];
​
指向类型决定了该指针变量+1跳过的字节长度,决定了*p取内容取出来的字节数

指针的大小

无论是int *类型的指针变量,还是char *类型的指针变量
在64位linux系统中恒定为8个字节大小
在32位linux系统中恒定位4个字节大小

指针变量和字符串

char *p = "hello";
p可以被修改
但*p不能被修改,因为*p表示的是一个字符串常量,常量不能被修改

指针变量与数组

int a[5] = {0,1,2,3,4};
int *p = a;
​
*p == a[0];
*(p + 1) == a[1];
*(p + n) == a[n];
-----------------
*p == a[0];
p++;
*p == a[1];
​
​
int a[5] = {0,1,2,3,4};
int *p = a;
​
p[0] == a[0]
p[1] == a[1]
偏移后
p += 2;
p[0] == a[2];
​
*(a + 1) == *( p + 1)
​
a与p不等价,p是一个指针变量;a是一个地址常量,是数组的首地址,不能和p一样进行自加操作
​
​

#include <stdio.h>
​
int main(int argc, char *argv[])
{
    /*
    char a[10] = "hello";
    char *p = &a[4];
    int i = 0;
    for( i = 0; i < 5; i++)
    {
        printf("%c", *p);
        p--;
    }
    putchar('\n');
    puts(a);
    */
​
    char a[10] = "hello";
    char b = 0;
    char *p = a;
    char *q = a;
    while( *(q + 1) != '\0')
    {
        q++;
    }
    int i = 0;
    for( i = 0; i < 2; i++)
    {
        b = *p;
        *p = *q;
        *q = b;
        p++;
        q--;
    }
    puts(a);
​
    p++;
    printf("%c\n", *(a + 1));
    return 0;
} 
strlen:

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    char buf[10] = {0};
    scanf("%s", buf);
    char *p = buf;
    int temp = 0;
    while( *p != '\0')
    {
        temp++;
        p++;
    }
    printf("temp:%d\n", temp);
    return 0;
} 
strcat:
#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    char a[10] = {0};
    char b[10] = {0};
    char *p = a;
    char *q = b;
​
    gets(a);
    gets(b);
    while( *p != '\0' )
    {
        p++;
    }
​
    while( *q != '\0')
    {
        *p = *q;
        p++;
        q++;
    }
    puts(a);
    puts(b);
    return 0;
} 
​
strcmp:
​
#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    char a[10] = {0};
    char b[10] = {0};
    char *p = a;
    char *q = b;
    gets(a);
    gets(b);
​
    int i = 0;
    for( i = 0;i < 10; i++)
    {
        if( *p == *q)
        {
            if( i == 9)
            {
                printf("0\n");
                break;
            }
            p++;
            q++;
        }
        else if( *p > *q)
        {
            printf("%d\n", *p - *q);
            break;
        }
        else
        {
            printf("%d\n", *p - *q);
        }
    }
    return 0;
}
strcpy:

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    char a[10] = {0};
    char b[10] = {0};
    char *p = a;
    char *q = b;
​
    gets(a);
    gets(b);
​
    do{
        *p = *q;
        p++;
    }while( *(q++) != '\0');
​
    puts(a);
    puts(b);
    return 0;
} 
​
​

const和指针

const int *p
*p不能被改变,即p所储存的地址 所对应的内存空间当中的内容 不能被改变
int const *p
*p不能被改变,即p所储存的地址 所对应的内存空间当中的内容 不能被改变
int * const p
p不能被改变,即p所储存的地址不能被改变
​
​

实现整型数组循环右移

#include <stdio.h>
#include <string.h>
​
int main(int argc, char *argv[])
{ 
    int a[6] = {1,2,3,4,5,6};
    int *p = &a[5];
    int cout = 0;
    scanf("%d", &cout);
    int i = 0;
    int j = 0;
    for( i = 0; i < cout; i++)
    {
        int temp = *p;
        for(j = 5;j >=1;j--)
        {
            a[j] = a[j-1];
        }
        a[0] = temp; 
    }
    for(i = 0;i<6;i++)
    {
        printf("a[%d]:%-3d",i,a[i]);
    }
    printf("\n");
    return 0;
}

void类型指针

int a = 10;
void *p = NULL;
p = &a;
printf("a:%d\n",*((int *)p));

指针的活用

大小端
低位的数据存放在低地址空间:小端存储
低位的数据存放在高地址空间:大端存储
​

#include <stdio.h>
​
int main(int argc, char *argv[])
{
    /*
    int a = 0x12345678;
    char *p = &a;
    p++;
    short *q = p;
    printf("*p:%#X\n", *q);
    */
​
    /*
    char a[6] = "hello";
    int *p = a;
    p++;
    printf("*p:%c\n", *((char *)p));
    */
​
    int b[2] = {10,20};
    char *p = b;
    p += 4;
    printf("*p:%d\n", *p);
    return 0;
} 

指针变量和二维数组

#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    int a[2][3] = {0,1,2,3,4,5};
    int *p = a[0];
    int sum = 0;
    int i = 0;
    for(i = 0; i < 6;i++)
    {
        sum += *(p + i);
        printf("%d\n", *(p+i));
    }
​
    printf("sum:%d\n", sum);
    return 0;
} 

数组指针

数组指针也叫行指针,本质是一个指针,指向了数组的某一行
​
基本形式:
数据类型    (*数组指针名)[该数组的列号(该二维数组每个一维数组的元素个数)]
例:
int (*p)[3];
​
例:
​
#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    /*
    int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
    int (*p)[4] = a;
    printf("%d\n",**p);
    */
​
    /*
    int a[10] = {0,1,2,3,4,5,6,7,8,9};
    int *p = a;
    printf("%d\n", *p);
​
    int b[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
    int (*q)[4] = b;
    printf("%d\n", *(*(q+1)+1));
​
    */
​
    char buf[5][10] = {"hello","world","amazing","happy","angry"};
    char (*p)[10] = buf;
​
    printf("%c\n", **p);
    printf("%c\n", *(*(p+1)+1));
    printf("%c\n", *(*(p+2)+2));
    printf("%c\n", *(*(p+3)+3));
    printf("%s\n", *(p+4));
    printf("%s\n", *(p+2)+4);
    printf("%c\n", *p[2]);
    printf("%c\n", (*p)[2]);
​
    
    return 0;
} 
​

指针数组

本质是一个数组,用于存储指针
​
数据类型    *数组名[元素个数]
例:

int *a[3];
​
#include <stdio.h>
​
int main(int argc, char *argv[])
{ 
    int a[3] = {0,1,2};
    int *p1 = &a[0];
    int *p2 = &a[1];
    int *p3 = &a[2];
    int *b[3] = {p1,p2,p3};
​
    printf("%d\n",*b[2]);
    return 0;
} 
 

二级指针

用来保存指针变量的地址的指针变量
​
int a = 10;
int *p = &a;
int **q = &p;

函数

封装好的代码块,可以被直接调用,相当于在让别人帮我做事情

printf() scanf() strlen() strcpy() strcat() main()

函数的基本形式:
​
int main(int argc, char *argv[])
{ 
​
    return 0;
} 
​
数据类型    函数名(形式参数)
{
    代码块
    return 返回值;
}
​
数据类型:和返回值所对应,返回值是什么类型,数据类型就写什么类型,当数据类型为void类型的时候,函数没有返回值,如果不写,则默认为int类型
​
函数名:最好见名知意,并且和标识符的命名规则保持一致
​
形式参数:将实际参数的值拷贝一份过来放到形式参数里,并且形式参数在进入函数时声明定义初始化,函数结束时释放,相当于局部变量;数据类型不能省略。
​
返回值:和数据类型保持一致,可以写常量,也可以写变量。
​
​
​

函数的声明和定义

函数的定义要放在主函数上面;如果放在主函数下面需要先将函数声明;
void func(void);
​
int main()
{
    
    return 0;
}
void func(void)
{
​
}

函数的使用

函数名(实际参数);
#include <stdio.h>
​
int func(char a, int n)
{
    //char a = 'Q';
    int i = 0;
    for(i = 0; i < n; i++)
        printf("%c", a);
    putchar('\n');
    return n;
}
​
int main(int argc, char *argv[])
{ 
    char a = 'Q';
    int temp = func(a, 7);
    printf("temp:%d\n", temp);
    //putchar('\n');
    //putchar(a);
    //putchar('\n');
    return 0;
}
​
​

函数的形式参数

1、值传递

传递普通变量时,只会把变量的值拷贝一份给形式参数,但变量本身没有传递给形参

2、地址传递

将变量的地址传递给形式参数,可以通过直接操作内存地址的方式来操作原本变量

3、全局变量

全局变量寿命周期长,可以用于函数中
#include <stdio.h>
​
int add(int a, int b)
{
    return a + b;
}
​
int sub(int a, int b)
{
    return a - b;
}
​
int mul(int a, int b)
{
    return a * b;
}
​
int div(int a, int b)
{
    return a/b;
}
​
void swap(int *a, int *b)
{
    int c = 0;
    c = *a;
    *a = *b;
    *b = c;
}
​
int c = 10;
int d = 20;
void swap2(int a, int b)
{
    int temp = 0;
    temp = a;
    a = b;
    b = temp;
}
​
int main(int argc, char *argv[])
{ 
    int a = 10;
    int b = 20;
    printf("before-------\n");
    printf("a:%d    b:%d\n", a, b);
    swap2(c, d);
    printf("after--------\n");
    printf("c:%d    d:%d\n", c, d);
    return 0;
} 

函数的形式参数不仅能传普通的变量,还可以传地址进去

例:
如果想在形式参数中设置地址传递:
void func(int *a, int *b);
或者数组名也可以作为地址传递进去,作为地址传递
int *p[3];
如果要传递&p[0]为参数则函数应该以int **的类型来接受
void func1(int **a, int *b);

函数的返回值能不能写地址?(即函数的数据类型能不能作为指针变量类型)?

指针函数

基本形式:
​
int *   函数名(形式参数)
{
    代码块;
    return 地址;
}
​
本质是一个函数,返回值为地址(指针)
​
return返回的地址
1、全局变量
2、static修饰的局部变量
3、字符串常量的首地址
4、形式参数传递进来的地址
​
核心思想:不允许返回一个没有被申请使用权的地址,即不能去非法访问内存
​
非法访问内存:使用了未被申请的内存空间,如数组的越界访问
内存泄露:申请了内存空间后,没有进行释放
内存溢出:内存空间满了,申请空间时申请失败

函数指针

本质是一个指针,用于保持函数的入口,可以通过指针来调用函数
基本形式:
​
函数的数据类型 *函数指针名(形式参数)
​
int (*p)(int a, int b) = add;
p(形式参数1,形式参数2) == add(形式参数1,形式参数2
​
形式参数的个数和类型必须和函数保持一致

回调函数

将一个函数的函数名作为参数传入另一个函数中供其使用

#include <stdio.h>
​
int add(int a,int b)
{
    return a+b;
}
​
int sub(int a,int b)
{
    return a-b;
}
int mul(int a,int b)
{
    return a*b;
}
int div(int a,int b)
{
    return a/b;
}
​
int jjcc(int a, int b, int (*p)(int c,int d))
{
    return p(a,b);
}
​
int main(int argc, char *argv[])
{ 
    //int (*p)(int a,int b) = add;
    //printf("%d\n", p(10,20));
    printf("%d\n",jjcc(20,10,sub));
    return 0;
} 
​

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值