ps:先举三反一,再举一反三,学习就应该是这样,先模仿,再改进,最后实现自己的创意! –小甲鱼论坛

C语言中各种数据类型所占用的存储空间的大小

  • int—32位 四个字节

  • float —32位 四个字节

  • char—8位 一个字节

  • double—-64位 8个字节

  • bool—-16位 2个字节

  • long—-32位 四个字节

整数类型

下表列出了关于标准整数类型的存储大小和值范围的细节:

类型存储大小值范围
char1 字节-128 到 127 或 0 到 255
unsigned char1 字节0 到 255
signed char1 字节-128 到 127
int2 或 4 字节-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int2 或 4 字节0 到 65,535 或 0 到 4,294,967,295
short2 字节-32,768 到 32,767
unsigned short2 字节0 到 65,535
long4 字节-2,147,483,648 到 2,147,483,647
unsigned long4 字节0 到 4,294,967,295

浮点类型

下表列出了关于标准浮点类型的存储大小、值范围和精度的细节:

类型存储大小值范围精度
float4 字节1.2E-38 到 3.4E+386 位小数
double8 字节2.3E-308 到 1.7E+30815 位小数
long double16 字节3.4E-4932 到 1.1E+493219 位小数

void 类型

void 类型指定没有可用的值。它通常用于以下三种情况下:

序号类型与描述
1函数返回为空 C 中有各种函数都不返回值,或者您可以说它们返回空。不返回值的函数的返回类型为空。例如 void exit (int status);
2函数参数为空 C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void);
3指针指向 void 类型为 void * 的指针代表对象的地址,而不是类型。例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。

C语言运算符优先级

优先级运算符名称或含义使用形式结合方向说明
1[]数组下标数组名[常量表达式]左到右
()圆括号(表达式)/函数名(形参表)
.成员选择(对象)对象.成员名
->成员选择(指针)对象指针->成员名
2-负号运算符-表达式右到左单目运算符
(类型)强制类型转换(数据类型)表达式
++自增运算符++变量名/变量名++单目运算符
自减运算符–变量名/变量名–单目运算符
*取值运算符*指针变量单目运算符
&取地址运算符&变量名单目运算符
!逻辑非运算符!表达式单目运算符
~按位取反运算符~表达式单目运算符
sizeof长度运算符sizeof(表达式)
3/表达式/表达式左到右双目运算符
*表达式*表达式双目运算符
%余数(取模)整型表达式/整型表达式双目运算符
4+表达式+表达式左到右双目运算符
-表达式-表达式双目运算符
5«左移变量«表达式左到右双目运算符
»右移变量»表达式双目运算符
6>大于表达式>表达式左到右双目运算符
>=大于等于表达式>=表达式双目运算符
<小于表达式<表达式双目运算符
<=小于等于表达式<=表达式双目运算符
7==等于表达式==表达式左到右双目运算符
!=不等于表达式!= 表达式双目运算符
8&按位与表达式&表达式左到右双目运算符
9^按位异或表达式^表达式左到右双目运算符
10|按位或表达式|表达式左到右双目运算符
11&&逻辑与表达式&&表达式左到右双目运算符
12||逻辑或表达式||表达式左到右双目运算符
13?:条件运算符表达式1? 表达式2: 表达式3右到左三目运算符
14=赋值运算符变量=表达式右到左
/=除后赋值变量/=表达式
*=乘后赋值变量*=表达式
%=取模后赋值变量%=表达式
+=加后赋值变量+=表达式
-=减后赋值变量-=表达式
«=左移后赋值变量«=表达式
»=右移后赋值变量»=表达式
&=按位与后赋值变量&=表达式
^=按位异或后赋值变量^=表达式
|=按位或后赋值变量|=表达式
15,逗号运算符表达式,表达式,…左到右从左向右顺序运算

注:同一优先级的运算符,运算次序由结合方向所决定。

break和continue语句区别

  • break语句

  • 结束当前循环

  • continue语句

    • 跳过本次循环

二维下标直接索引和指针间接索引的相互转化

1
2
3
4
*array + i = array[i]
*(*(array + i) + j) = array[i][j]
*(*(*(array + i) + j) + K) = array[i][j][k]
			...

二维数组和数组指针的关系

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
//
// Created by Luenci on 2019/10/5.
//

#include <stdio.h>


int main(){

    int array[2][3] = {{0, 1, 2}, {4, 5, 6}};
    int (*p)[3] = array;

    printf("**(p+1): %d\n", **(p+1));
    printf("**(array+1): %d\n", **(array+1));
    printf("array[1][0]: %d\n", array[1][0]);
    printf("*(*(p+1)+2): %d\n", *(*(p+1)+2));
    printf("*(*(array+1)+2): %d\n", *(*(array+1)+2));
    printf("array[1][2]: %d\n", array[1][2]);


    return 0;
}

void指针和NULL指针

Void指针

  • void指针我们把它称之为通用指针,就是可以指向任意类型的数据。也就是说,任何类型的指针都可以赋值给void指针。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
//
// Created by Luenci on 2019/10/5.
//
#include <stdio.h>

int main(){
    int num = 1024;
    int *pi = &num;
    char *ps = "Luenci";
    void *pv;

    pv = pi;
    printf("pi:%p, pv:%p\n", pi,pv);
    printf("*pv:%d\n", *(int *)pv);

    pv = (char *)ps;
    printf("ps:%p, pv:%p\n", ps, pv);
    printf("*pv:%s\n", (char *)pv);

    return 0;
}

NULL指针

  • 当你还不清楚要将指针初始值为什么地址时,请先将它初始化为**NULL**;在对指针进行解引用时,先检查该指针是否为NULL。这种策略可以为今后编写大型程序 节省大量的 调试时间。

  • 1
    
    #define  NULL ((void *)0)
    
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//
// Created by Luenci on 2019/10/5.
//
#include <stdio.h>

int null_pointer(){
    int *p1;
    int *p2 = NULL;

    printf("%d\n", *p1);
    printf("%d\n", *p2);

    return 0;
}

指向指针的指针

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    int main(){
    
        int num = 520;
        int *p = &num;
        int **pp = &p;
    
        printf("num: %d\n", num);
        printf("*p: %d\n", *p);
        printf("**PP: %d\n", **pp);
        printf("&p: %p, pp:%p\n", &p, pp);
        printf("&num:%p, p:%p, *pp:%p\n", &num,p,*pp);
    
        return 0;
    }
    
        out:
        num: 520
    	*p: 520
    	**PP: 520
    	&p: 0061FF14, pp:0061FF14
    	&num:0061FF18, p:0061FF18, *pp:0061FF18
    

指针数组和指向指针的指针

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    
    int pointer_array(){
    
        char *cBooks[] = {
                "<c程序设计语言>",
                "<c专家编程>",
                "<c和指针>",
                "<c prime plus>",
                "<C study>"
        };
    
        char **byLuenci;
        char **byGood[4];
    
        byLuenci = &cBooks[4];
        byGood[0] = &cBooks[0];
        byGood[1] = &cBooks[1];
        byGood[2] = &cBooks[2];
        byGood[3] = &cBooks[3];
    
        printf("Luenci拥有的书: %s\n", *byLuenci);
        printf("Luenci喜欢的图书有:\n");
    
        int i;
    
        for (int i = 0; i < 4; ++i) {
            printf("%s\n",*byGood[i]);
        }
    
    
        return 0;
    }
    
    out: 
    Luenci拥有的书: <C study>
    Luenci喜欢的图书有:
    <c程序设计语言>
    <c专家编程>
    <c和指针>
    <c prime plus>
    

常量

  • 定义
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
520,‘a, 3.14

#define price 520
#define A 'a'
#define PI 3.14
    
// 使用const关键字修饰
    
const int price = 520;
const char A = 'a';
const float pi = 3.14;

指向常量的指针

  • 指针可以修改为指向不同的常量
  • 指针可以修改为指向不同的变量
  • 可以通过解引用来读取指针指向的数据
  • 不可以通过解引用修改指针指向的数据
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int main(){

    int num =520;
    const int cnum = 880;
    const int *pc = &cnum;

    printf("cnum:%d, &cum:%p \n", cnum, &cnum);
    printf("*pc:%d, pc:%p\n", *pc, pc);

    pc = &num;
    printf("num:%d, &num:%p \n", num, &num);
    printf("*pc:%d, pc:%p\n", *pc, pc);


    return 0;
}

函数

结构

1
2
3
4
类型名 函数名(参数列表)
{
	函数体
}

函数的声明

  • 所谓的声明(Declaration),就是告诉编译器我要是用这个函数,你现在没有找到它的定义不要紧,请不要报错,稍后我将会把它补上。

  • 函数的参数和返回值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//
// Created by Luenci on 2019/10/8.
//

#include <stdio.h>

void print_c();
int sum(int n);
int max(int x, int y);


int max(int x, int y){
    // a?b:c 当a的值为真时,返回b的值;否则返回c的值
    return x > y ? x:y;
}

int sum(int n){
    int result = 0;

    do
        {
            result += n;
        }while (n-- > 0);

    return result;

}

void print_c(){
    printf(" ###### \n");
    printf("##    ##\n");
    printf("##      \n");
    printf("##      \n");
    printf("##    ##\n");
    printf(" ###### \n");
}

int main(){

    print_c();

    int n,x,y;
//    printf("请输入n的值:");
    printf("请输入x,y的值:");
//    scanf("%d", &n);
    scanf("%d%d", &x, &y);

//    printf("1+2+3+...+(n-1)+n的结果是:%d\n",sum(n));
    printf("x,y中最大值的值为:%d\n", max(x,y));

    return 0;
}

形参和实参

  • 用于数据传输,形参就相当与一个占位符,只在函数内部有效。

传值和传址

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//
// Created by Luenci on 2019/10/8.
//

#include <stdio.h>

void swap(int *x, int *y);

void swap(int *x, int *y){
    int *temp;

    printf("In swap 互换前:x %d, y %d\n", *x,*y);
    temp = *x;
    *x = *y;
    *y = temp;
    printf("In swap 互换后:x %d, y %d\n", *x,*y);

}

int main(){
    int x = 3, y = 5;
    printf("In main 互换前:x %d, y %d\n", x,y);
    swap(&x,&y);
    printf("In main 互换后:x %d, y %d\n", x,y);
    return 0;
}

传数组

  • 实际上传过去的是数组的地址
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void get_array(int b[10]){

    printf("sizeof b: %d\n", sizeof(b));

}

int main(){

    int a[10] = {0,1,2,3,4,5,6,7,8,9};
    printf("sizeof a:%d\n", sizeof(a));
    get_array(a);
    
out:
sizeof a:40
sizeof b: 4

可变参数

  • 引入 头文件**#include<stdarg.h>**
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//
// Created by Luenci on 2019/10/8.
//

#include <stdio.h>
#include <stdarg.h>

int sum(int n, ...);

int sum(int n, ...){

    int i;
    int sum = 0;
    va_list vap;

    va_start(vap, n);
    for(i = 0; i < n; i++){
        sum += va_arg(vap, int);
    }
    va_end(vap);

    return sum;
}

int main(){

    int result,result2,result3;

    result = sum(3,1,2,-3);
    printf("result:%d\n", result);

    result2 = sum(4,2,2,4,2);
    printf("result2:%d\n", result2);

    result3 = sum(4,2,8,4,-2);
    printf("result3:%d\n", result3);

    return 0;
}

指针函数

  • 使用指针变量作为函数的返回值,就是指针函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//
// Created by Luenci on 2019/10/8.
//

#include <stdio.h>

char *getchartchar();

char *getchart(char c){
    switch (c)
    {
        case 'A':
            return "Apple";
        case 'B':
            return "Banana";
        case 'C':
            return "Cat";
        case 'D':
            return "Dog";
        default:
            return "None";
    }

}

int main(){
    char input;

    printf("请输入一个字母:");
    scanf("%c", &input);

    printf("%s\n", getchart(input));


    return 0;
}

in:A
out:Apple
  • PS:不要返回局部变量的指针

函数指针

  • 指针函数 - - > *int p();
  • 函数指针 - - > *int (p)();

函数指针作为参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//
// Created by Luenci on 2019/10/8.
//
#include <stdio.h>

int add(int, int);
int sub(int, int);
int clac(int (*fp)(int, int), int, int);

int add(int num1, int num2){
    return num1 + num2;
}

int sub(int num1, int num2){
    return num1 - num2;
}

int clac(int (*fp)(int, int), int num1, int num2){ 
    return (*fp)(num1, num2);
}

int main(){
    
    printf("3 + 5 = %d\n", clac(add, 3,5));
    printf("3 - 5 = %d\n", clac(sub, 3,5));

    return 0;
}

函数指针作为返回值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//
// Created by Luenci on 2019/10/10.
//
#include <stdio.h>

int add(int, int);
int sub(int, int);
int calc(int (*)(int, int), int, int);
int (*slect(char))(int,int);

int add(int num1, int num2){
    return num1 + num2;
}

int sub(int num1, int num2){
    return num1 - num2;
}

int calc(int (*fp)(int, int),int num1, int num2){

    return (*fp)(num1, num2);
}

int (*slect(char op))(int, int){

    switch(op){
        case '+':
            return add;
        case '-':
            return sub;
    }
}


int main(){
    int num1, num2;
    char op;
    int (*fp)(int, int);

    printf("请输入一个式子 (如 1+3) :");
    scanf("%d%c%d", &num1, &op, &num2);

    fp = slect(op);
    printf("%d %c %d = %d\n", num1, op, num2, calc(fp, num1, num2));

    return 0;
}

局部变量和全局变量

局部变量

  • 不同函数的变量无法相互访问

全局变量

  • 如果不对全局变量进行初始化,那么它会自动初始化为0.
  • 如果函数在内部存在一个与全局变量同名的局部变量,编译器不会报错,而是在函数中屏蔽全局变量。

exsten关键字

  • 告诉编译器我等下会定义变量,不要报错

不要大量的使用全局变量

  • 使用全局变量会使你的程序占用更多的内存,因为全局变量从定义时候开始,知道程序退出才释放。
  • 污染命名空间
  • 提高了程序耦合性

作用域和链接属性

  • 当变量被定义在不同的位置时,它的作用域的范围是不一样的,这个作用范围就是我们所说的作用域
  • C语言编译器可以确认四种不同类型的作用域:
    • 代码块的作用域
    • 文件作用域
    • 原型作用域
    • 函数作用域

代码块作用域(Block scope)

  • 在代码块中定义的变量,具有代码块作用域。作用范围是从变量定义的位置开始,到标志代码块结束的大括号(})处。

文件作用域(file scope)

  • 任何在代码块之外声明的标识符都具有文件作用域,作用域的范围是从他们的声明位置开始,到文件的结尾处都是可以访问的。

原型作用域(prototype scope)

  • 原型作用域只适用于那些在函数原型中声明的参数名。

函数作用域(function scope)

  • 函数作用域只适合于goto语句的标签,作用将goto语句得标签限制在同一个函数内部,以防止出现重名的标签。

定义和声明

  • 当一个变量被定义的时候,编译器为变量申请内存空间并填充值
  • 当一个变量被声明的时候,编译器就知道该变量被定义在其他地方
  • 声明是通知编译器该变量名及相关的类型已经存在,不需要再为此申请内存空间。
  • 局部变量即是定义又是声明
  • 定义只能来一次,否则就叫做重复定义某个同名的变量;而声明可以由你很多次。

链接属性

  • external(外部的)

  • 多个文件中声明的同名标识符表示一个实体

  • internal(内部的)

    • 单个文件中声明的同名标识符表示同一个实体
  • none(无)

    • 声明的同名标识符被当做独立的不同实体
  • 只有具备文件作用域的标识符才能拥有externalinternal的链接属性,其他作用域的标识符都是none属性。

  • 默认情况下,具备文件的作用域的标识符拥有external属性。也就是说该标识符允许跨文件访问。对于external属性的标识符,无论在不同文件中声明多少次,表示的都是一个实体。

  • 使用static关键字可以使得原先拥有的external属性的标识符变为internal属性。这里有两点需要注意:

    • 使用static关键字修改链接属性,只对具有文件作用域的标识符生效(对于拥有其他作用域的标志符是另一种功能)
    • 链接属性只能修改一次,也就是说一旦将标识符的链接属性改为internal,就无法变回external

生存期和存储类型

C语言变量拥有两种生存周期

  • 静态存储器

  • 自动存储器

  • 具有文件作用域的变量属于静态存储器,函数也属于静态存储器。属于静态存储器的变量在程序执行期间将一直占据存储空间,直到程序关闭才释放

  • 具有代码块作用域的变量一般情况下属于自动存储器。属于自动存储期的变量在代码块结束时将自动释放。

存储类型

  • 存储类型其实是指存储变量值的内存类型,C语言提供了五种类型。
  • auto
  • register
  • static
  • extern
  • typedef