C基础


C语言

概述


#include <stdio.h>
int main(int argc, const char * argv[]) 
{
    printf("Hello, World!\n");
    return 0;
}

return

return代表函数执行完毕,返回return代表函数的终止
如果main定义的时候前面是int,那么return后面就需要写一个整数;如果main定义的时候前面是void,那么return后面什么也不需要写
在main函数中return 0代表程序执行成功,return -1代表程序执行失败
int main()和void main()在C语言中是一样的,但C++只接受int main这种定义方式

include

#include的意思是头文件包含,#include <stdio.h>代表包含stdio.h这个头文件
使用C语言库函数需要提前包含库函数对应的头文件,如这里使用了printf()函数,需要包含stdio.h头文件
#include< > 与 #include ""的区别:
< > 表示系统直接按系统指定的目录检索
"" 表示系统先在 "" 指定的路径(没写路径代表当前路径)查找头文件,如果找不到,再按系统指定的目录检索

C代码编译成可执行程序经过4步.c

预处理:宏定义展开、头文件展开、条件编译等,同时将代码中的注释删除,这里并不会检查语法(.i

编译:检查语法,将预处理后文件编译生成汇编文件(.s)

汇编:将汇编文件生成目标文件(二进制文件)(.o)

链接:C语言写的程序是需要依赖各种库的,所以编译之后还需要把库链接到最终的可执行程序中去()

32个关键字

数据类型关键字(12个)
char、short、int、long、float、double、unsigned、signed、struct、union、enum、void

控制语句关键字(12个)
if、else、switch、case、default、for、do、while、break、continue、goto、return

存储类关键字(5个)
auto、extern、register、static、constant

其他关键字(3个)
sizeof、typedef、volatile

变量


声明变量不需要建立存储空间,如:extern int a;定义变量需要建立存储空间,如:int b;

#include <stdio.h>
int main(int argc, const char* argv[])
{
    {
        //extern 关键字只做声明,不能做任何定义
        //声明一个变量a,a在这里没有建立存储空间
        extern int a;
        //a = 10;	//err, 没有空间,就不可以赋值
        int b = 10;	//定义一个变量b,b的类型为int,b赋值为10
        return 0;
    }
}

define、const

#include <stdio.h>
#define MAX 10 //声明了一个常量,名字叫MAX,值是10,常量的值一旦初始化不可改
int main()
{
    int a;	//定义了一个变量,其类型为int,名字叫a
    const int b = 10; //定义一个const常量,名为叫b,值为10
    //b = 11; //err,常量的值不能改变
    //MAX = 100;	//err,常量的值不能改变
    a = MAX;//将abc的值设置为MAX的值
    a = 123;
    printf("%d\n", a); //打印变量a的值
    return 0;
}

通过 #define 定义的常量,是根据值来匹配数据类型的。const修饰的常量是不安全,可以通过指针来修改。


进制


#include <stdio.h>
int main()
{
    int a = 123;		//十进制方式赋值
    int b = 0123;		//八进制方式赋值, 以数字0开头
    int c = 0xABC;	//十六进制方式赋值
    //如果在printf中输出一个十进制数那么用%d,八进制用%o,十六进制是%x
    printf("十进制:%d\n", a);
    printf("八进制:%o\n", b);	//%o,为字母o,不是数字
    printf("十六进制:%x\n", c);
    return 0;
}

sizeof


sizeof不是函数,所以不需要包含任何头文件,它的功能是计算一个数据类型的大小,单位为字节。sizeof的返回值为size_t。size_t类型在32位操作系统下是unsigned int,是一个无符号的整数。

#include <stdio.h>
int main()
{
    int a;
    int b = sizeof(a);//sizeof得到指定值占用内存的大小,单位:字节
    printf("b = %d\n", b);
    size_t c = sizeof(a);
    printf("c = %u\n", c);//用无符号数的方式输出c的值
    return 0;
}

int


打印格式

%hd	输出short类型
%d	输出int类型
%l	输出long类型
%ll	输出long long类型
%hu	输出unsigned short类型
%u	输出unsigned int类型
%lu	输出unsigned long类型
%llu	输出unsigned long long类型

代码

#include <stdio.h>
int main()
{
    short a = 10;
    int b = 10;
    long c = 10l; //或者10L
    long long d = 10ll; //或者10LL
    printf("sizeof(a) = %u\n", sizeof(a));
    printf("sizeof(b) = %u\n", sizeof(b));
    printf("sizeof(c) = %u\n", sizeof(c));
    printf("sizeof(c) = %u\n", sizeof(d));

    printf("short a = %hd\n", a);
    printf("int b = %d\n", b);
    printf("long c = %ld\n", c);
    printf("long long d = %lld\n", d);

    unsigned short a2 = 20u;
    unsigned int b2 = 20u;
    unsigned long c2 = 20ul;
    unsigned long long d2 = 20ull;

    printf("unsigned short a = %hu\n", a2);
    printf("unsigned int b = %u\n", b2);
    printf("unsigned long c = %lu\n", c2);
    printf("unsigned long long d = %llu\n", d2);
    return 0;
}

有符号数是最高位为符号位,0代表正数,1代表负数。

#include <stdio.h>
int main()
{
    signed int a = -1089474374; //定义有符号整型变量a
    printf("%X\n", a); //结果为 BF0FF0BA
    //B       F      0        F       F     0        B	      A
    //1011 1111 0000 1111 1111 0000 1011 1010
    return 0;
}

无符号数最高位不是符号位,而就是数的一部分,无符号数不可能是负数。无符号数,可以增大数的表达最大值。

#include <stdio.h>
int main()
{
    unsigned int a = 3236958022; //定义无符号整型变量a
    printf("%X\n", a); //结果为 C0F00F46
    return 0;
}

char


字符变量实际上并不是把该字符本身放到变量的内存单元中去,而是将该字符对应的ASCII编码放到变量的存储单元中。char的本质就是一个1字节大小的整型

#include <stdio.h>
int main()
{
    char ch = 'a';
    printf("sizeof(ch) = %u\n", sizeof(ch));
    printf("ch[%%c] = %c\n", ch); //打印字符
    printf("ch[%%d] = %d\n", ch); //打印‘a’ ASCII的值

    char A = 'A';
    char a = 'a';
    printf("a = %d\n", a);		//97
    printf("A = %d\n", A);	//65
    printf("A = %c\n", 'a' - 32); //小写a转大写A
    printf("a = %c\n", 'A' + 32); //大写A转小写a

    ch = ' ';
    printf("空字符:%d\n", ch); //空字符ASCII的值为32
    printf("A = %c\n", 'a' - ' '); //小写a转大写A
    printf("a = %c\n", 'A' + ' '); //大写A转小写a
    return 0;
}

float、double


#include <stdio.h>
int main()
{
    //传统方式赋值
    float a = 3.14f; //或3.14F
    double b = 3.14;
    printf("a = %f\n", a);
    printf("b = %lf\n", b);

    //科学法赋值,e3相当于1000,e-3相当于0.001
    a = 3.2e3f; //3.2*1000 = 32000,e可以写E
    printf("a1 = %f\n", a);
    a = 100e-3f; //100*0.001 = 0.1
    printf("a2 = %f\n", a);
    a = 3.1415926f;
    printf("a3 = %f\n", a); //结果为3.141593
    return 0;
}

类型限定符

//extern:声明一个变量,extern声明的变量没有建立存储空间
extern int a;
//const:定义一个常量,常量的值不能修改。
const int a = 10;
//volatile	防止编译器优化代码
//register	定义寄存器变量,提高效率。register是建议型的指令,而不是命令型的指令,如果CPU有空闲寄存器,那么register就生效,如果没有空闲寄存器,那么register无效。

字符串常量


字符串是内存中一段连续的char空间,以'\0'(数字0)结尾。

每个字符串的结尾,编译器会自动的添加一个结束标志位'\0',即 "a" 包含两个字符'a''\0'


运算符


C语言的比较运算中,“真”用数字“1”来表示,“假”用数字“0”来表示。

->	对象指针->成员名	//成员选择(指针)
&	&变量名	//取地址运算符

三目运算符


#include <stdio.h>
int main()
{
    int a = 10;
    int b = 20;
    int c;
    c = (a > b ? a : b);
    printf("c = %d\n", c);
    return 0;
}

冒泡排序


#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
    int arr[10] = { 7,4,2,3,5,8,9,6,1,10 };
    int len = sizeof(arr) / sizeof(arr[0]) - 1;
    //冒泡排序   从小到大
    for (int i = 0; i <= len; i++)
    {
        for (int j = 0; j < len - i; j++)
        {
            if (arr[j] < arr[j + 1])
            {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    for (int i = 0; i < 10; i++)
    {
        printf("%d\n", arr[i]);
    }
    system("pause");
    return EXIT_SUCCESS;
}

goto语句


无条件跳转,尽量少用。

#include <stdio.h>
int main()
{
    goto End; //无条件跳转到End的标识
    printf("aaaaaaaaa\n");
End:
    printf("bbbbbbbb\n");
    return 0;
}

数组


数组就是在内存中连续的相同类型的变量空间。同一个数组所有的成员都是相同的数据类型,同时所有的成员在内存中的地址是连续的。

#include <stdio.h>
int main()
{
    int a[10];//定义了一个数组,名字叫a,有10个成员,每个成员都是int类型
    //a[0]…… a[9],没有a[10]
    //没有a这个变量,a是数组的名字,但不是变量名,它是常量
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        a[i] = i; //给数组赋值
    }
    //遍历数组,并输出每个成员的值
    for (i = 0; i < 10; i++)
    {
        printf("%d ", a[i]);
    }
    printf("\n");
    return 0;
}

一维数组的初始化


在定义数组的同时进行赋值,称为初始化。全局数组若不初始化,编译器将其初始化为零。局部数组若不初始化,内容为随机值。

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//定义一个数组,同时初始化所有成员变量
int a[10] = { 1, 2, 3 };//初始化前三个成员,后面所有元素都设置为0
int a[10] = { 0 };//所有的成员都设置为0

//[]中不定义元素个数,定义时必须初始化
int a[] = { 1, 2, 3, 4, 5 };//定义了一个数组,有5个成员

数组名


数组名是一个地址的常量,代表数组中首元素的地址

#include <stdio.h>
int main()
{
    int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//定义一个数组,同时初始化所有成员变量
    printf("a = %p\n", a);
    printf("&a[0] = %p\n", &a[0]);//相等的

    int n = sizeof(a); //数组占用内存的大小,10个int类型,10 * 4  = 40
    int n0 = sizeof(a[0]);//数组第0个元素占用内存大小,第0个元素为int,4

    //遍历数组
    int i = 0;
    for (i = 0; i < sizeof(a) / sizeof(a[0]); i++)
    {
        printf("%d ", a[i]);
    }
    printf("\n");
    return 0;
}

数组元素个数

int (size_t) unsigned int 个数  = sizeof(数组名)/sizeof(数组元素 | 数组数据类型) 

数组地址

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//定义一个数组,同时初始化所有成员变量
printf("a = %p\n", a);
printf("&a[0] = %p\n", &a[0]);

一维数组的最值


#include <stdio.h>
int main()
{
    int a[] = { 1, -2, 3,-4, 5, -6, 7, -8, -9, 10 };//定义一个数组,同时初始化所有成员变量
    int i = 0;
    int max = a[0];
    for (i = 0; i < sizeof(a) / sizeof(a[0]); i++)
    {
        if (a[i] > max)
        {
            max = a[i];
        }
    }
    printf("数组中最大值为:%d\n", max);
    return 0;
}

一维数组的逆置


#include <stdio.h>
int main()
{
    int a[] = { 1, -2, 3,-4, 5, -6, 7, -8, -9, 10 };//定义一个数组,同时初始化所有成员变量
    int i = 0;
    int j = sizeof(a) / sizeof(a[0]) - 1;
    int tmp;
    while (i < j)
    {
        tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
        i++;
        j--;
    }
    for (i = 0; i < sizeof(a) / sizeof(a[0]); i++)
    {
        printf("%d ", a[i]);//10 -9 -8 7 -6 5 -4 3 -2 1
    }
    printf("\n");
    return 0;
}

冒泡法排序


#include <stdio.h>
int main()
{
    int a[] = { 1, -2, 3,-4, 5, -6, 7, -8, -9, 10 };//定义一个数组,同时初始化所有成员变量
    int i = 0;
    int j = 0;
    int n = sizeof(a) / sizeof(a[0]);
    int tmp;
    for (i = 0; i < n - 1; i++)
    {
        for (j = 0; j < n - i - 1; j++)//内循环的目的是比较相邻的元素,把大的放到后面
        {
            if (a[j] > a[j + 1])
            {
                tmp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = tmp;
            }
        }
    }

    for (i = 0; i < n; i++)
    {
        printf("%d ", a[i]);
    }
    printf("\n");
    return 0;
}

二维数组


//常量表达式1表示第一维下标的长度,常量表达式2表示第二维下标的长度。
类型说明符 数组名[常量表达式1][常量表达式2]

在内存中并不存在二维数组,二维数组实际的硬件存储器是连续编址的,也就是说内存中只有一维数组,即放完一行之后顺次放入第二行,和一维数组存放方式是一样的。

实现

#include <stdio.h>
int main()
{
    //定义了一个二维数组,名字叫a
    //由3个一维数组组成,这个一维数组是int [4]
    //这3个一维数组的数组名分别为a[0],a[1],a[2]
    int a[3][4];
    //给数组每个元素赋值
    int i = 0;
    int j = 0;
    int num = 0;
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 4; j++)
        {
            a[i][j] = num++;
        }
    }

    //遍历数组,并输出每个成员的值
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 4; j++)
        {
            printf("%d, ", a[i][j]);
        }
        printf("\n");
    }
    return 0;
}

二维数组的初始化


//分段赋值 	
int a[3][4] = {{ 1, 2, 3, 4 },{ 5, 6, 7, 8, },{ 9, 10, 11, 12 }};

//连续赋值
int a[3][4] = { 1, 2, 3, 4 , 5, 6, 7, 8, 9, 10, 11, 12};

//可以只给部分元素赋初值,未初始化则为0
int a[3][4] = { 1, 2, 3, 4};

//所有的成员都设置为0
int a[3][4] = {0};

//[]中不定义元素个数,定义时必须初始化
int a[][4] = { 1, 2, 3, 4, 5, 6, 7, 8};

数组名


数组名是一个地址的常量,代表数组中首元素的地址。

#include <stdio.h>

int main()
{
    //定义了一个二维数组,名字叫a。二维数组是本质上还是一维数组,此一维数组有3个元素。每个元素又是一个一维数组int[4]
    int a[3][4] = { 1, 2, 3, 4 , 5, 6, 7, 8, 9, 10, 11, 12 };
    //数组名为数组首元素地址,二维数组的第0个元素为一维数组。第0个一维数组的数组名为a[0]
    printf("a = %p\n", a);
    printf("a[0] = %p\n", a[0]);//二者相等

    //测二维数组所占内存空间,有3个一维数组,每个一维数组的空间为4*4。sizeof(a) = 3 * 4 * 4 = 48
    printf("sizeof(a) = %d\n", sizeof(a));

    //测第0个元素所占内存空间,a[0]为第0个一维数组int[4]的数组名,4*4=16
    printf("sizeof(a[0]) = %d\n", sizeof(a[0]));

    //测第0行0列元素所占内存空间,第0行0列元素为一个int类型,4字节
    printf("sizeof(a[0][0]) = %d\n", sizeof(a[0][0]));

    //求二维数组行数,3
    printf("i = %d\n", sizeof(a) / sizeof(a[0]));

    // 求二维数组列数,4
    printf("j = %d\n", sizeof(a[0]) / sizeof(a[0][0]));

    //求二维数组行*列总数,12
    printf("n = %d\n", sizeof(a) / sizeof(a[0][0]));
    return 0;
}

字符数组与字符串


C语言中没有字符串这种数据类型,可以通过char的数组来替代;字符串一定是一个char的数组,但char的数组未必是字符串;数字0(和字符‘\0’等价)结尾的char数组就是一个字符串,但如果char数组没有以数字0结尾,那么就不是一个字符串,只是普通字符数组,所以字符串是一种特殊的char的数组。

#include <stdio.h>

int main()
{
    char c1[] = { 'c', ' ', 'p', 'r', 'o', 'g' }; //普通字符数组
    printf("c1 = %s\n", c1); //乱码,因为没有’\0’结束符

    //以‘\0’(‘\0’就是数字0)结尾的字符数组是字符串
    char c2[] = { 'c', ' ', 'p', 'r', 'o', 'g', '\0' };
    printf("c2 = %s\n", c2);

    //字符串处理以‘\0’(数字0)作为结束符,后面的'h', 'l', 'l', 'e', 'o'不会输出
    char c3[] = { 'c', ' ', 'p', 'r', 'o', 'g', '\0', 'h', 'l', 'l', 'e', 'o', '\0' };
    printf("c3 = %s\n", c3);
    return 0;
}

字符串初始化


#include <stdio.h>

// C语言没有字符串类型,通过字符数组模拟
// C语言字符串,以字符‘\0’, 数字0
int main()
{
    //不指定长度, 没有0结束符,有多少个元素就有多长
    char buf[] = { 'a', 'b', 'c' };
    printf("buf = %s\n", buf);	//乱码

    //指定长度,后面没有赋值的元素,自动补0
    char buf2[100] = { 'a', 'b', 'c' };
    printf("buf2 = %s\n", buf2);//buf2 = abc

    //所有元素赋值为0
    char buf3[100] = { 0 };

    //char buf4[2] = { '1', '2', '3' };//数组越界

    char buf5[50] = { '1', 'a', 'b', '0', '7' };
    printf("buf5 = %s\n", buf5);//buf5 = 1ab07

    char buf6[50] = { '1', 'a', 'b', 0, '7' };
    printf("buf6 = %s\n", buf6);// 1ab

    char buf7[50] = { '1', 'a', 'b', '\0', '7' };
    printf("buf7 = %s\n", buf7);// 1ab

    //使用字符串初始化,编译器自动在后面补0,常用
    char buf8[] = "agjdslgjlsdjg";

    //'\0'后面最好不要连着数字,有可能几个数字连起来刚好是一个转义字符
    //'\ddd'八进制字义字符,'\xdd'十六进制转移字符
    // \012相当于\n
    char str[] = "\012abc";
    printf("str == %s\n", str);

    return 0;
}

字符串追加拼接


#include <stdio.h>

int main()
{
    char str1[] = "abcdef";
    char str2[] = "123456";
    char dst[100];

    int i = 0;
    while (str1[i] != 0)
    {
        dst[i] = str1[i];
        i++;
    }

    int j = 0;
    while (str2[j] != 0)
    {
        dst[i + j] = str2[j];
        j++;
    }
    dst[i + j] = 0; //字符串结束符
    printf("dst = %s\n", dst);
    return 0;
}

随机数


#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main()
{
    time_t tm = time(NULL);//得到系统时间
    srand((unsigned int)tm);//随机种子只需要设置一次即可
    int r = rand();
    printf("r = %d\n", r);
    return 0;
}

strlen()


#include <string.h>计算指定指定字符串的长度,不包含字符串结束符‘\0’

#include <stdio.h>
#include <string.h>
int main()
{
    char str[] = "abcdefg";
    int n = strlen(str);
    printf("n = %d\n", n);
    return 0;
}

strcpy_s


include <string.h> char *strcpy_s(char *dest, const char *src);

功能:把src所指向的字符串复制到dest所指向的空间中,’\0’也会拷贝过去。返回值:成功:返回dest字符串的首地址;失败:NULL。

注意:如果参数dest所指的内存空间不够大,可能会造成缓冲溢出的错误情况。

#include <stdio.h>
#include <string.h>
int main()
{
    char dest[20] = "123456789";
    char src[] = "hello world";
    strcpy_s(dest, src);
    printf("%s\n", dest);
    return 0;
}

strncpy_s


#include <string.h> char *strncpy_s(char *dest, const char *src, size_t n);把src指向字符串的前n个字符复制到dest所指向的空间中,是否拷贝结束符看指定的长度是否包含’\0’。返回值:成功:返回dest字符串的首地址;失败:NULL。

#include <stdio.h>
#include <string.h>
int main()
{
    char dest[20];
    char src[] = "hello world";

    strncpy_s(dest, src, 5);
    printf("%s\n", dest);

    dest[5] = '\0';
    printf("%s\n", dest);
    return 0;
}

strcat_s


#include <string.h> char *strcat_s(char *dest, const char *src);将src字符串连接到dest的尾部,‘\0’也会追加过去。返回值:成功:返回dest字符串的首地址;失败:NULL。

#include <stdio.h>
#include <string.h>
int main()
{
    char str[20] = "123";
    char src[] = "hello world";
    strcat_s(str, src);
    printf("%s\n", str);
    return 0;
}

strncat_s


#include <string.h> char *strncat(char *dest, const char *src, size_t n);将src字符串前n个字符连接到dest的尾部,‘\0’也会追加过去。返回值:成功:返回dest字符串的首地址;失败:NULL。

#include <stdio.h>
#include <string.h>
int main()
{
    char str[20] = "123";
    char src[] = "hello world";
    strncat_s(str, src,5);
    printf("%s\n", str);
    return 0;
}

strcmp和strncmp


比较 s1 和 s2 的大小,比较的是字符ASCII码大小。返回值:相等:0; 大于:>0; 小于:<0。

#include <stdio.h>
#include <string.h>
int main()
{
    char str1[] = "hello world";
    char str2[] = "hello mike";

    if (strcmp(str1, str2) == 0)
    {
        printf("str1==str2\n");
    }
    else if (strcmp(str1, str2) > 0)
    {
        printf("str1>str2\n");
    }
    else
    {
        printf("str1<str2\n");
    }
    return 0;
}


#include <stdio.h>
#include <string.h>
int main()
{
    char str1[] = "hello world";
    char str2[] = "hello mike";

    if (strncmp(str1, str2, 5) == 0)
    {
        printf("str1==str2\n");
    }
    else if (strncmp(str1, str2, 5) > 0)
    {
        printf("str1>str2\n");
    }
    else
    {
        printf("str1<str2\n");
    }
    return 0;
}

sprintf_s


#include <stdio.h>
int main()
{
    char dst[100] = { 0 };
    int a = 10;
    char src[] = "hello world";
    printf("a = %d, src = %s", a, src);
    printf("\n");

    int len = sprintf_s(dst, "a = %d, src = %s", a, src);
    printf("dst = \" %s\"\n", dst);// dst = " a = 10, src = hello world"
    printf("len = %d\n", len);//25
    return 0;
}

strchr


#include <string.h> char *strchr(const char *s, int c);在字符串s中查找字母c出现的位置。返回值:成功:返回第一次出现的c地址;失败:NULL。

#include <stdio.h>
#include <string.h>
int main()
{
    char src[] = "ddda123abcd";
    char* p = strchr(src, 'a');
    printf("p = %s\n", p);
    return 0;
}

strstr


#include <string.h> char *strstr(const char *haystack, const char *needle);在字符串haystack中查找字符串needle出现的位置。返回值:成功:返回第一次出现的needle地址;失败:NULL。

#include <stdio.h>
#include <string.h>
int main()
{
    char src[] = "ddddabcd123abcd333abcd";
    char* p = strstr(src, "abcd");
    printf("p = %s\n", p);//p = abcd123abcd333abcd
    return 0;
}

strstr


#include <stdlib.h> int atoi(const char *nptr);atoi()会扫描nptr字符串,跳过前面的空格字符,直到遇到数字或正负号才开始做转换,而遇到非数字或字符串结束符(‘\0’)才结束转换,并将结果返回返回值。返回值:成功转换后整数

类似的函数有:atof():把一个小数形式的字符串转化为一个浮点数。atol():将一个字符串转化为long类型。

#include <stdio.h>
#include <stdlib.h>
int main()
{
    char str1[] = "-10";
    int num1 = atoi(str1);
    printf("num1 = %d\n", num1);//num1 = -10

    char str2[] = "0.123";
    double num2 = atof(str2);
    printf("num2 = %lf\n", num2);//num2 = 0.123000
    return 0;
}

函数

函数的定义


函数定义的一般形式:

返回类型 函数名(形式参数列表)
{
    数据定义部分;
    执行语句部分;
}

如果没有形参,圆括号内容为空,或写一个void关键字


函数的形参和实参


形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参也不能使用。单向传递,只由实参传给形参,而不能由形参传回来给实参。在调用函数时,编译系统临时给形参分配存储单元。调用结束后,形参单元被释放。实参单元与形参单元是不同的单元。调用结束后,形参单元被释放,函数调用结束返回主调函数后则不能再使用该形参变量。实参单元仍保留并维持原值。因此,在执行一个被调用函数时,形参的值如果发生改变,并不会改变主调函数中实参的值

实参可以是常量、变量或表达式。


函数的声明


函数定义的位置在主调函数之后,则必须在调用此函数之前对被调用的函数作声明。

所谓函数声明,就是在函数尚在未定义的情况下,事先将该函数的有关信息通知编译系统,相当于告诉编译器,函数在后面定义,以便使编译能正常进行。注意:一个函数只能被定义一次,但可以声明多次。

#include <stdio.h>
int max(int x, int y); // 函数的声明,分号不能省略
// int max(int, int); // 另一种方式

int main()
{
    int a = 10, b = 25, num_max = 0;
    num_max = max(a, b); // 函数的调用
    printf("num_max = %d\n", num_max);
    return 0;
}

// 函数的定义
int max(int x, int y)
{
    return x > y ? x : y;
}

exit


在main函数中调用exit和return结果是一样的,但在子函数中调用return只是代表子函数终止了,在子函数中调用exit,那么程序终止。


分文件编程


max.h文件

extern int max(int a, int b);

max.c文件

int max(int x, int y)
{
    return x > y ? x : y;
}

main.c文件

#include <stdio.h>
#include "max.h"
int main()
{
    int a = 10, b = 25, num_max = 0;
    num_max = max(a, b); // 函数的调用
    printf("num_max = %d\n", num_max);
    return 0;
}

防止头文件重复包含


方法一:

#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__
// 声明语句
#endif

方法二:

#pragma once
// 声明语句

指针

int  i	定义整形变量
int *p	定义一个指向int的指针变量
int a[10]	定义一个有10个元素的数组,每个元素类型为int
int *p[10]	定义一个有10个元素的数组,每个元素类型为int*
int func()	定义一个函数,返回值为int型
int *func()	定义一个函数,返回值为int *型
int **p	定义一个指向int的指针的指针,二级指针

内存地址和指针


将内存抽象成一个很大的一维字符数组。编码就是对内存的每一个字节分配一个32位或64位的编号(与32位或者64位处理器相关)。这个内存编号我们称之为内存地址。

内存中的每一个数据都会分配相应的地址:char:占一个字节分配一个地址;int: 占四个字节分配四个地址。

内存区的每一个字节都有一个编号,这就是“地址”。

如果在程序中定义了一个变量,在对程序进行编译或运行时,系统就会给这个变量分配内存单元,并确定它的内存地址(编号)

指针的实质就是内存“地址”。指针就是地址,地址就是指针。指针是内存单元的编号,指针变量是存放地址的变量。

指针也是一种数据类型,指针变量也是一种变量。指针变量指向谁,就把谁的地址赋值给指针变量。“*”操作符操作的是指针变量指向的内存空间。

#include <stdio.h>
int main()
{
    int a = 0;
    char b = 100;
    printf("%p, %p\n", &a, &b); //打印a, b的地址

    //int *代表是一种数据类型,int*指针类型,p才是变量名
    //定义了一个指针类型的变量,可以指向一个int类型变量的地址
    int* p;
    p = &a;//将a的地址赋值给变量p,p也是一个变量,值是一个内存地址编号
    printf("%d\n", *p);//p指向了a的地址,*p就是a的值

    char* p1 = &b;
    printf("%c\n", *p1);//*p1指向了b的地址,*p1就是b的值
    return 0;
}

通过指针间接修改变量的值


#include <stdio.h>
int main()
{
    int a = 0;
    int b = 11;
    int* p = &a;

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

    p = &b;
    *p = 22;
    printf("b = %d, *p = %d\n", b, *p);//b = 22, *p = 22
    return 0;
}

指针大小


使用sizeof()测量指针的大小,得到的总是:4或8。sizeof()测的是指针变量指向存储地址的大小。

在32位平台,所有的指针(地址)都是32位(4字节)。在64位平台,所有的指针(地址)都是64位(8字节)。


野指针和空指针


指针变量也是变量,是变量就可以任意赋值,不要越界即可(32位为4字节,64位为8字节),但是,任意数值赋值给指针变量没有意义,因为这样的指针就成了野指针,此指针指向的区域是未知(操作系统不允许操作此指针指向的内存区域)。所以,野指针不会直接引发错误,操作野指针指向的内存区域才会出问题。

int a = 100;
int* p;
p = a; //把a的值赋值给指针变量p,p为野指针, ok,不会有问题,但没有意义
p = 0x12345678; //给指针变量p赋值,p为野指针, ok,不会有问题,但没有意义
*p = 1000;  //操作野指针指向未知区域,内存出问题,err

但是,野指针和有效指针变量保存的都是数值,为了标志此指针变量没有指向任何变量(空闲可用),C语言中,可以把NULL赋值给此指针,这样就标志此指针为空指针,没有任何指针。NULL是一个值为0的宏常量:

int *p = NULL;

万能指针


void *指针可以指向任意变量的内存空间:

void* p = NULL;
int a = 10;
p = (void*)&a; //指向变量时,最好转换为void *
//使用指针变量指向的内存时,转换为int *
*((int*)p) = 11;
printf("a = %d\n", a);//11

const修饰的指针变量


指针常量和常量的指针

#include <stdio.h>
int main()
{
    int a = 100;
    int b = 200;

    //指向常量的指针
    //修饰*,指针指向内存区域不能修改,指针指向可以变
    const int* p1 = &a; //等价于int const *p1 = &a;
    //*p1 = 111; //err
    p1 = &b; //ok

    //指针常量
    //修饰p2,指针指向不能变,指针指向的内存可以修改
    int* const p2 = &a;
    //p2 = &b; //err
    *p2 = 222; //ok
    return 0;
}

指针和数组


数组名字是数组的首元素地址,但它是一个常量:

int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
printf("a = %p\n", a);//a = 0000002A5F6FFB48
printf("&a[0] = %p\n", &a[0]);//&a[0] = 0000002A5F6FFB48
//a = 10; //err, 数组名只是常量,不能修改

指针操作数组元素

#include <stdio.h>
int main()
{
    int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int i = 0;
    int n = sizeof(a) / sizeof(a[0]);
    for (i = 0; i < n; i++)
    {
        //printf("%d, ", a[i]);
        printf("%d, ", *(a + i));//1, 2, 3, 4, 5, 6, 7, 8, 9,
    }
    printf("\n");
    int* p = a; //定义一个指针变量保存a的地址
    for (i = 0; i < n; i++)
    {
        p[i] = 2 * i;
    }
    for (i = 0; i < n; i++)
    {
        printf("%d, ", *(p + i));//0, 2, 4, 6, 8, 10, 12, 14, 16,
    }
    printf("\n");
    return 0;
}

指针加法运算:指针计算不是简单的整数相加,如果是一个int *,+1的结果是增加一个int的大小;如果是一个char *,+1的结果是增加一个char大小。

int a;
int* p = &a;
printf("%d\n", p);//896988804
p += 2;//移动了2个int
printf("%d\n", p);//896988812

char b = 0;
char* p1 = &b;
printf("%d\n", p1);//896988812
p1 += 2;//移动了2个char
printf("%d\n", p1);//896988812
return 0;

通过改变指针指向操作数组元素:

#include <stdio.h>
int main()
{
    int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int i = 0;
    int n = sizeof(a) / sizeof(a[0]);
    int *p = a;
    for (i = 0; i < n; i++)
    {
        printf("%d, ", *p);
        p++;
    }
    printf("\n");//1, 2, 3, 4, 5, 6, 7, 8, 9,
    return 0;
}

指针减法运算

int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int* p2 = &a[2]; //第2个元素地址
int* p1 = &a[1]; //第1个元素地址
printf("p1 = %p, p2 = %p\n", p1, p2);

int n1 = p2 - p1; //n1 = 1
int n2 = (int)p2 - (int)p1; //n2 = 4
printf("n1 = %d, n2 = %d\n", n1, n2);

指针数组,它是数组,数组的每个元素都是指针类型。

//指针数组
int* p[3];
int a = 1;
int b = 2;
int c = 3;
int i = 0;

p[0] = &a;
p[1] = &b;
p[2] = &c;

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

多级指针


C语言允许有多级指针存在,在实际的程序中一级指针最常用,其次是二级指针。二级指针就是指向一个一级指针变量地址的指针。

int a = 10;
int* p = &a; //一级指针
*p = 100; //*p就是a

int** q = &p;
//*q就是p
//**q就是a

int*** t = &q;
//*t就是q
//**t就是p
//***t就是a

指针和函数

函数形参改变实参的值


#include <stdio.h>
void swap1(int x, int y)
{
    int tmp;
    tmp = x;
    x = y;
    y = tmp;
    printf("x = %d, y = %d\n", x, y);//x = 5, y = 3
}

void swap2(int* x, int* y)
{
    int tmp;
    tmp = *x;
    *x = *y;
    *y = tmp;
}

int main()
{
    int a = 3;
    int b = 5;
    swap1(a, b); //值传递
    printf("a = %d, b = %d\n", a, b);//a = 3, b = 5

    a = 3;
    b = 5;
    swap2(&a, &b); //地址传递
    printf("a2 = %d, b2 = %d\n", a, b);//a2 = 5, b2 = 3
    return 0;
}

数组名做函数参数


数组名做函数参数,函数的形参会退化为指针。

//void printArrary(int a[10], int n)
//void printArrary(int a[], int n)
void printArrary(int* a, int n)
{
    int i = 0;
    for (i = 0; i < n; i++)
    {
        printf("%d, ", a[i]);//1, 2, 3, 4, 5, 6, 7, 8, 9,
    }
    printf("\n");
}

int main()
{
    int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int n = sizeof(a) / sizeof(a[0]);

    //数组名做函数参数
    printArrary(a, n);
    return 0;
}

指针做为函数的返回值


#include <stdio.h>

int a = 10;
int* getA()
{
    return &a;
}
int main()
{
    *(getA()) = 111;
    printf("a = %d\n", a);//a = 111
    return 0;
}

指针和字符串


char str[] = "hello world";
char* p = str;
*p = 'm';
p++;
*p = 'i';
printf("%s\n", str);//millo world

p = "mike jiang";
printf("%s\n", p);//mike jiang

char* q = "test";
printf("%s\n", q);//test

字符指针做函数参数

#include <stdio.h>

void mystrcat(char* dest, const char* src)
{
    int len1 = 0;
    int len2 = 0;
    while (dest[len1])
    {
        len1++;
    }
    while (src[len2])
    {
        len2++;
    }

    int i;
    for (i = 0; i < len2; i++)
    {
        dest[len1 + i] = src[i];
    }
}

int main()
{
    char dst[100] = "hello mike";
    char src[] = "123456";
    mystrcat(dst, src);
    printf("dst = %s\n", dst);//dst = hello mike123456
    return 0;
}

const修饰的指针,变量从左往右看,跳过类型,看修饰哪个字符,如果是*, 说明指针指向的内存不能改变,如果是指针变量,说明指针的指向不能改变,指针的值不能修改。

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

int main(void)
{
    //const修饰一个变量为只读
    const int a = 10;
    //a = 100; //err

    //指针变量, 指针指向的内存, 2个不同概念
    char buf[] = "aklgjdlsgjlkds";

    //从左往右看,跳过类型,看修饰哪个字符
    //如果是*, 说明指针指向的内存不能改变
    //如果是指针变量,说明指针的指向不能改变,指针的值不能修改
    const char* p = buf;
    // 等价于上面 char const *p1 = buf;
    //p[1] = '2'; //err
    p = "agdlsjaglkdsajgl"; //ok

    char* const p2 = buf;
    p2[1] = '3';
    //p2 = "salkjgldsjaglk"; //err

    //p3为只读,指向不能变,指向的内存也不能变
    const char* const p3 = buf;
    return 0;
}

查找”abcd”个数-while

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

int main(void)
{
    char* p = "11abcd111122abcd333abcd3322abcd3333322qqq";
    int n = 0;

    while ((p = strstr(p, "abcd")) != NULL)
    {
        //能进来,肯定有匹配的子串
        //重新设置起点位置
        p = p + strlen("abcd");
        n++;
        if (*p == 0) //如果到结束符
        {
            break;
        }
    }
    printf("n = %d\n", n);
    return 0;
}

查找”abcd”个数-do-while

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

int main(void)
{
    char* p = "11abcd111122abcd333abcd3322abcd3333322qqq";
    int n = 0;
    do
    {
        p = strstr(p, "abcd");
        if (p != NULL)
        {
            n++; //累计个数
            //重新设置查找的起点
            p = p + strlen("abcd");

        }
        else //如果没有匹配的字符串,跳出循环
        {
            break;
        }
    } while (*p != 0); //如果没有到结尾
    printf("n = %d\n", n);
    return 0;
}

求非空字符串元素的个数:

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

int fun(char* p, int* n)
{
    if (p == NULL || n == NULL)
    {
        return -1;
    }

    int begin = 0;
    int end = strlen(p) - 1;

    //从左边开始
    //如果当前字符为空,而且没有结束
    while (p[begin] == ' ' && p[begin] != 0)
    {
        begin++; //位置从右移动一位
    }

    //从右往左移动
    while (p[end] == ' ' && end > 0)
    {
        end--; //往左移动
    }

    if (end == 0)
    {
        return -2;
    }

    //非空元素个数
    *n = end - begin + 1;
    return 0;
}

int main(void)
{
    char* p = "      abcddsgadsgefg      ";
    int ret = 0;
    int n = 0;

    ret = fun(p, &n);
    if (ret != 0)
    {
        return ret;
    }
    printf("非空字符串元素个数:%d\n", n);//非空字符串元素个数:14
    return 0;
}

字符串反转模型(逆置):

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

int inverse(char* p)
{
    if (p == NULL)
    {
        return -1;
    }
    char* str = p;
    int begin = 0;
    int end = strlen(str) - 1;
    char tmp;

    while (begin < end)
    {
        //交换元素
        tmp = str[begin];
        str[begin] = str[end];
        str[end] = tmp;

        begin++;  //往右移动位置
        end--;	    //往左移动位置
    }
    return 0;
}

int main(void)
{
    //char *str = "abcdefg"; //文件常量区,内容不允许修改
    char str[] = "abcdef";

    int ret = inverse(str);
    if (ret != 0)
    {
        return ret;
    }

    printf("str ========== %s\n", str);//str ========== fedcba
    return 0;
}

内存管理


类型	作用域	生命周期	存储位置
auto变量	一对{}内	当前函数	栈区
static局部变量	一对{}内	整个程序运行期	初始化在data段,未初始化在BSS段
extern变量	整个程序	整个程序运行期	初始化在data段,未初始化在BSS段
static全局变量	当前文件	整个程序运行期	初始化在data段,未初始化在BSS段
extern函数	整个程序	整个程序运行期	代码区
static函数	当前文件	整个程序运行期	代码区
register变量	一对{}内	当前函数	运行时存储在CPU寄存器
字符串常量	当前文件	整个程序运行期	data段

C代码经过预处理、编译、汇编、链接4步后生成一个可执行程序。

只有c语言将数组放在栈区


malloc()


#include <stdlib.h>
void *malloc(size_t size);

功能:在内存的动态存储区(堆区)中分配一块长度为size字节的连续区域,用来存放类型说明符指定的类型。分配的内存空间内容不确定,一般使用memset初始化。参数:size:需要分配内存大小(单位:字节)。返回值:成功:分配空间的起始地址,失败:NULL。


free


#include <stdlib.h>
void free(void *ptr);

功能:释放ptr所指向的一块内存空间,ptr是一个任意类型的指针变量,指向被释放区域的首地址。对同一内存空间多次释放会出错。
参数:ptr:需要释放空间的首地址,被释放区应是由malloc函数所分配的区域。返回值:无。


结构体


定义结构体变量的方式:先声明结构体类型再定义变量名;在声明类型的同时定义变量;直接定义结构体类型变量(无类型名)。

结构体类型:指定了一个结构体类型,它相当于一个模型,但其中并无具体数据,系统对之也不分配实际内存单元。

结构体变量:系统根据结构体类型(内部成员状况)为之分配空间。

//结构体类型的定义
struct stu
{
    char name[50];
    int age;
};

//先定义类型,再定义变量(常用)
struct stu s1 = { "mike", 18 };


//定义类型同时定义变量
struct stu2
{
    char name[50];
    int age;
}s2 = { "lily", 22 };

struct
{
    char name[50];
    int age;
}s3 = { "yuri", 25 };

结构体成员的使用


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

//结构体类型的定义
struct stu
{
    char name[50];
    int age;
};

int main()
{
    struct stu s1;

    //如果是普通变量,通过点运算符操作结构体成员
    strcpy(s1.name, "abc");
    s1.age = 18;
    printf("s1.name = %s, s1.age = %d\n", s1.name, s1.age);

    //如果是指针变量,通过->操作结构体成员
    strcpy((&s1)->name, "test");
    (&s1)->age = 22;
    printf("(&s1)->name = %s, (&s1)->age = %d\n", (&s1)->name, (&s1)->age);

    return 0;
}

结构体数组


#include <stdio.h>

//统计学生成绩
struct stu
{
    int num;
    char name[20];
    char sex;
    float score;
};

int main()
{
    //定义一个含有5个元素的结构体数组并将其初始化
    struct stu boy[5] = {
        { 101, "Li ping", 'M', 45 },
        { 102, "Zhang ping", 'M', 62.5 },
        { 103, "He fang", 'F', 92.5 },
        { 104, "Cheng ling", 'F', 87 },
        { 105, "Wang ming", 'M', 58 } };

    int i = 0;
    int c = 0;
    float ave, s = 0;
    for (i = 0; i < 5; i++)
    {
        s += boy[i].score;	//计算总分
        if (boy[i].score < 60)
        {
            c += 1;		//统计不及格人的分数
        }
    }

    printf("s=%f\n", s);//打印总分数	s=345.000000
    ave = s / 5;					//计算平均分数
    printf("average=%f\ncount=%d\n\n", ave, c); //打印平均分与不及格人数 average=69.000000 count=2


    for (i = 0; i < 5; i++)
    {
        printf(" name=%s,  score=%f\n", boy[i].name, boy[i].score);
        // printf(" name=%s,  score=%f\n", (boy+i)->name, (boy+i)->score);

    }
    return 0;
}

结构体套结构体


#include <stdio.h>

struct person
{
    char name[20];
    char sex;
};

struct stu
{
    int id;
    struct person info;
};

int main()
{
    struct stu s[2] = { 1, "lily", 'F', 2, "yuri", 'M' };

    int i = 0;
    for (i = 0; i < 2; i++)
    {
        printf("id = %d\tinfo.name=%s\tinfo.sex=%c\n", s[i].id, s[i].info.name, s[i].info.sex);
    }

    return 0;
}

结构体赋值


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

//结构体类型的定义
struct stu
{
    char name[50];
    int age;
};

int main()
{
    struct stu s1;

    //如果是普通变量,通过点运算符操作结构体成员
    strcpy(s1.name, "abc");
    s1.age = 18;
    printf("s1.name = %s, s1.age = %d\n", s1.name, s1.age);

    //相同类型的两个结构体变量,可以相互赋值
    //把s1成员变量的值拷贝给s2成员变量的内存
    //s1和s2只是成员变量的值一样而已,它们还是没有关系的两个变量
    struct stu s2 = s1;
    //memcpy(&s2, &s1, sizeof(s1));
    printf("s2.name = %s, s2.age = %d\n", s2.name, s2.age);

    return 0;
}

指向普通结构体变量的指针


#include<stdio.h>

//结构体类型的定义
struct stu
{
    char name[50];
    int age;
};

int main()
{
    struct stu s1 = { "lily", 18 };

    //如果是指针变量,通过->操作结构体成员
    struct stu* p = &s1;
    printf("p->name = %s, p->age=%d\n", p->name, p->age);//p->name = lily, p->age=18
    printf("(*p).name = %s, (*p).age=%d\n", (*p).name, (*p).age);//(*p).name = lily, (*p).age=18

    return 0;
}

堆区结构体变量


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

//结构体类型的定义
struct stu
{
    char name[50];
    int age;
};

int main()
{
    struct stu* p = NULL;

    p = (struct stu*)malloc(sizeof(struct  stu));

    //如果是指针变量,通过->操作结构体成员
    strcpy(p->name, "test");
    p->age = 22;

    printf("p->name = %s, p->age=%d\n", p->name, p->age);
    printf("(*p).name = %s, (*p).age=%d\n", (*p).name, (*p).age);

    free(p);
    p = NULL;

    return 0;
}

结构体套一级指针


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

//结构体类型的定义
struct stu
{
    char* name; //一级指针
    int age;
};

int main()
{
    struct stu* p = NULL;

    p = (struct stu*)malloc(sizeof(struct  stu));

    p->name = malloc(strlen("test") + 1);
    strcpy(p->name, "test");
    p->age = 22;

    printf("p->name = %s, p->age=%d\n", p->name, p->age);
    printf("(*p).name = %s, (*p).age=%d\n", (*p).name, (*p).age);

    if (p->name != NULL)
    {
        free(p->name);
        p->name = NULL;
    }

    if (p != NULL)
    {
        free(p);
        p = NULL;
    }

    return 0;
}

结构体普通变量做函数参数


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

//结构体类型的定义
struct stu
{
    char name[50];
    int age;
};

//函数参数为结构体普通变量
void set_stu(struct stu tmp)
{
    strcpy(tmp.name, "mike");
    tmp.age = 18;
    printf("tmp.name = %s, tmp.age = %d\n", tmp.name, tmp.age);
}

int main()
{
    struct stu s = { 0 };
    set_stu(s); //值传递
    printf("s.name = %s, s.age = %d\n", s.name, s.age);

    return 0;
}

结构体指针变量做函数参数


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

//结构体类型的定义
struct stu
{
    char name[50];
    int age;
};

//函数参数为结构体指针变量
void set_stu_pro(struct stu *tmp)
{
    strcpy(tmp->name, "mike");
    tmp->age = 18;
}

int main()
{
    struct stu s = { 0 };
    set_stu_pro(&s); //地址传递
    printf("s.name = %s, s.age = %d\n", s.name, s.age);

    return 0;
}

结构体数组名做函数参数


#include<stdio.h>

//结构体类型的定义
struct stu
{
    char name[50];
    int age;
};

//void set_stu_pro(struct stu tmp[100], int n)
//void set_stu_pro(struct stu tmp[], int n)
void set_stu_pro(struct stu *tmp, int n)
{
    int i = 0;
    for (i = 0; i < n; i++)
    {
        sprintf(tmp->name, "name%d%d%d", i, i, i);
        tmp->age = 20 + i;
        tmp++;
    }
}

int main()
{
    struct stu s[3] = { 0 };
    int i = 0;
    int n = sizeof(s) / sizeof(s[0]);
    set_stu_pro(s, n); //数组名传递

    for (i = 0; i < n; i++)
    {
        printf("%s, %d\n", s[i].name, s[i].age);
    }

    return 0;
}

const修饰结构体指针形参变量


//结构体类型的定义
struct stu
{
    char name[50];
    int age;
};

void fun1(struct stu* const p)
{
    //p = NULL; //err
    p->age = 10; //ok
}

//void fun2(struct stu const*  p)
void fun2(const struct stu* p)
{
    p = NULL; //ok
    //p->age = 10; //err
}

void fun3(const struct stu* const p)
{
    //p = NULL; //err
    //p->age = 10; //err
}

共用体(联合体)


联合union是一个能在同一个存储空间存储不同类型数据的类型;联合体所占的内存长度等于其最长成员的长度,也有叫做共用体;

同一内存段可以用来存放几种不同类型的成员,但每一瞬时只有一种起作用;

共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员的值会被覆盖;

共用体变量的地址和它的各成员的地址都是同一地址。

#include <stdio.h>

//共用体也叫联合体 
union Test
{
    unsigned char a;
    unsigned int b;
    unsigned short c;
};

int main()
{
    //定义共用体变量
    union Test tmp;

    //1、所有成员的首地址是一样的
    printf("%p, %p, %p\n", &(tmp.a), &(tmp.b), &(tmp.c));//0000003825CFF6B4, 0000003825CFF6B4, 0000003825CFF6B4

    //2、共用体大小为最大成员类型的大小
    printf("%lu\n", sizeof(union Test));//4

    //3、一个成员赋值,会影响另外的成员
    //左边是高位,右边是低位
    //低位放低地址,高位放高地址
    tmp.b = 0x44332211;

    printf("%x\n", tmp.a); //11
    printf("%x\n", tmp.c); //2211

    tmp.a = 0x00;
    printf("short: %x\n", tmp.c); //2200
    printf("int: %x\n", tmp.b); //44332200

    return 0;
}

枚举


枚举:将变量的值一一列举出来,变量的值只限于列举出来的值的范围内。

enum  枚举名
{
    枚举值表
};

在枚举值表中应列出所有可用值,也称为枚举元素。枚举值是常量,不能在程序中用赋值语句再对它赋值。枚举元素本身由系统定义了一个表示序号的数值从0开始顺序定义为0,1,2 …。

#include <stdio.h>

enum weekday
{
    sun = 2, mon, tue, wed, thu, fri, sat
};

enum bool
{
    flase, true
};

int main()
{
    enum weekday a, b, c;
    a = sun;
    b = mon;
    c = tue;
    printf("%d,%d,%d\n", a, b, c);//2,3,4

    enum bool flag;
    flag = true;

    if (flag == 1)
    {
        printf("flag为真\n");//flag为真
    }
    return 0;
}

typedef


为一种数据类型(基本类型或自定义数据类型)定义一个新名字,不能创建新类型。与#define不同,typedef仅限于数据类型,而不是能是表达式或具体的值。#define发生在预处理,typedef发生在编译阶段。

#include <stdio.h>

typedef int INT;
typedef char BYTE;
typedef BYTE T_BYTE;
typedef unsigned char UBYTE;

typedef struct type
{
    UBYTE a;
    INT b;
    T_BYTE c;
}TYPE, * PTYPE;

int main()
{
    TYPE t;
    t.a = 254;
    t.b = 10;
    t.c = 'c';

    PTYPE p = &t;
    printf("%u, %d, %c\n", p->a, p->b, p->c);//254, 10, c

    return 0;
}


文章作者:
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 !
  目录