C

作者: 放肆滴微笑 | 来源:发表于2019-12-15 22:26 被阅读0次

C 99 据说是1999年所定义的

引入头文件

里面只有函数的声明,没有实现,编译的时候,会去对应的so文件找到函数的实现

#include <stdio.h>

基本数据类型

有符号(默认定义变量就是有符号)和无符号
signed 有符号 unsigned无符号

  • int %d
  • short
  • long
  • float
  • double
  • char
  • bool 非0 就是true 0是flase 非Null就是true 等于null就是false
类型 占位 字节
short %d 2
int %d 4
long %ld 4
float %f 4
double %lf 8
char %c 1
字符串 %s
十六进制 %x
八进制 %o

为什么 int 和 long 一模一样长,还有这两者的出现呢?
答:long类型 和 int类型,在早期16为电脑的时候 int是2字节,long是4字节,而计算机经过多年的发展,一般是32位,64位,就造成了int 和 long一样长了,如果想使用8位 则可以long long xxx 这样声明

使用sizeof来运算获得类型在不同平台准确字节

double d = 1.12342;
printf("double 字节%d\n", sizeof(d));

如果只取小数点后2位,只需要在%f前面加上.2

long double ld01 = 16.123456;
printf("ld01=%.2f,字节:%d\n", ld01, sizeof(ld01));

vim xxx.c 创建一个文件

编译c文件

gcc xxx.c -o xxx

字符串

字符串的拼接,需要定义一个数组

    char strChar[200];
    sprintf(strChar,"今天是%d号\n",11); // 拼接到char 数组
    printf("%s\n",strChar);

数组

1、格式是这样的(array[] = {},array[6] = ),不能这样([] array)
2、必须给定 数组的大小,或者直接初始化

  • 数组是一块连续的内存空间
  • 数组内存地址和数组首位内存地址一样
int test[8];
int test[]={1,2,3,4,5};

for循环数组

    int test[] = {1,3,4,2,4};

    printf("数组首位地址:%#x\n",test);
    for (int i=0;i< sizeof(test)/ sizeof(int);i++) {
        printf("内存地址:%#x\n", (test + i));
        printf("内存地址对应的值:%x\n", * (test + i));
    }


数组首位地址:0x5f19f5f0
内存地址:0x5f19f5f0
内存地址:0x5f19f5f4
内存地址:0x5f19f5f8
内存地址:0x5f19f5fc
内存地址:0x5f19f600

动态内存申请 只要是动态内存申请,则在堆取,剩下的都在栈区

    calloc 
    需要头文件
    #include <stdbool.h>

     //在堆中申请 10个int类型的内存空间,也就是10*4 = 40字节
    int *p = calloc(10, sizeof(int));
    // 清空内存,因为内存可能是别人剩下的,里面还会有原来的值
    memset(p, NULL, 10 * sizeof(int));
    
    //释放堆内存,申请了,用完要释放
    free(p); 
    // 这个时候p 属于野指针,可以让p = 0 或者 null
    p = NULL;

C 和 C++ ?

C面向过程,一个一个的函数,没有类,没有面向对象
C++面向对象 思想和 Java的一模一样,开始有类

字符串

使用字符数组存储字符串

//字符数组    
char str[] = {'c', 'h', 'i', 'n', 'a', '\0'};   // 后面写0,就能结束
char str1[6] = {'c', 'h', 'i', 'n', 'a'};   // 直接写6个大小,字符5位,第6位就是0
char str2[10]="china";  // 直接双引号就是字符串
str[0]='w';
str1[0]='w';
str2[0]='w';
printf("str %s\n", str);
printf("str1 %s\n", str1);
printf("str2 %s\n", str2);
-------------------------------------
str whina
str1 whina
str2 whina

使用字符指针

char *str = "i XXX";
printf("*str %s\n",str);

*str i XXX

字符串指针和数组字符串 区别,字符串指针不能修改内存,数组可以

内存分配

C里面内存分配分为静态内存和动态内存
静态内存放在栈中,一般比较小,比如几M(存放确定的常数)自动释放,超过会有stack overflow错误,如

int a[1024 * 1024 * 10];

动态内存放在堆中,大小为内存的80%,程序员手动释放
创建一个数组,动态指定数组的大小,相当于java的集合

  • 静态内存分配,分配内存大小是固定的。
    问题:1、容易超出栈内存的最大值,从而stack overflow错误
    2、为了防止内存不够用,会开辟更多的内存,容易浪费内存
  • 动态内存分配,在程序运行过程中,动态内存需要指定内存大小,手动释放,释放后还可以继续使用

动态创建一个数组

    int len;
    printf("输入一个长度");
    scanf("%d", &len);
    int *p = malloc(len * sizeof(int));

    int i = 0;
    for (; i < len; i++) {
        *(p + i) = rand() % 100;
        printf("p==%d---%#x\n", p[i], &p[i]);
    }


输入一个长度5
p==7---0x38500000
p==49---0x38500004
p==73---0x38500008
p==58---0x3850000c
p==30---0x38500010

动态创建数组,并改变数组的大小,
存在问题,
1、缩小数组大小(内存),缩小的数据会丢失
2、扩大,内存会是连续的
3、扩大,1.如果当前内存段后面有需要扩大的空间,直接扩展这段内存空间,realloc返回的指针,地址也是原指针的地址
2.如果当前内存段后面空间不够需要扩大的空间,那么就使用堆后面第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据释放掉,返回新的内存地址
3.如果申请失败,返回NULL,原来的指针仍然有效

int len;
    printf("输入一个长度");
    scanf("%d", &len);
    int *p = malloc(len * sizeof(int));

    int i = 0;
    for (; i < len; i++) {
        *(p + i) = rand() % 100;
        printf("p==%d---%#x\n", p[i], &p[i]);
    }

    int len2;
    printf("输入一个长度");
    scanf("%d", &len2);
    int *p2 = realloc(p, (len + len2) * sizeof(int));
    i = 0;
    for (; i < len + len2; i++) {
        *(p2 + i) = rand() % 100;
        printf("p==%d---%#x\n", p2[i], &p2[i]);
    }

//p 不用再释放了,因为p2释放了,p也就释放了,p2本来就是对p的重新增长
    //if (p != NULL) {
      //  free(p);
        //p = NULL;
    //}
    if (p2 != NULL) {
        free(p2);
        p2 = NULL;
    }

输入一个长度5
p==7---0x38500000
p==49---0x38500004
p==73---0x38500008
p==58---0x3850000c
p==30---0x38500010
输入一个长度3
p==72---0x38500000
p==44---0x38500004
p==78---0x38500008
p==23---0x3850000c
p==9---0x38500010
p==40---0x38500014
p==65---0x38500018
p==92---0x3850001c

内存分配细节

  • 不能多次释放同一个指针,
  • 释放完后,指针仍然有值,需要给指针=NULL
  • 内存泄露,获取到的内存,如果需要再赋值,需要再赋值前进行free,要不相当于内存没有释放,系统认为还在使用
    int *p1 = malloc(1024 * 1024 * sizeof(int));
    free(p1); // 需要释放
    p1 = NULL;
    printf("p1 = %#x\n",p1);
    p1 = malloc(1024 * 1024 * sizeof(int) * 2); // 重新赋值
    printf("p1 = %#x\n",p1);
    free(p1);
    p1 = NULL;

头文件 .h文件,对外暴露 #include ""引入的是目录下可以看到的自己写的<>引入系统C配置的环境

引入头文件后,在.c文件中写头文件的函数,会有左右切换的符号,可以和头文件和当前写的文件进行交换。


image.png

之后再使用这个头文件,就直接在使用地方引入头文件,调用会调用实现头文件的.c文件
include 并不等于卷的import java.lang.String。
而是将include <xxx.h> 里的所有代码都复制一遍,放到include的地方。
所以如果不用就不要调用不用的h文件,要不会造成体积增大,在预处理器阶段就完成了,还不到编译器就结束了

宏 可以理解为java中的常量

宏不需要分号结尾

#define NAME "wx"

预处理器,代码的指向流程,可以作为代码块选择 或者 注释使用

if 和 #endif配套

void main() {
#define NAME "wx"

#if 0   // 非0 是true  0就是false  所以打印else if 但是预编译就直接有颜色显示
    printf("打印 if \n");
#else if
    printf("打印 else if \n");
#endif   // 一定会执行
    printf("打印 endif \n");
}


打印 else if 
打印 endif 
image.png

配合宏

#define DEBUG

    //ifdef 表示有定义   ifndef 表示没有定义 n == not
#ifdef DEBUG
    printf("配合宏,有定义DEBUG");
#else
    printf("配合宏");
#endif
    printf("必须执行");


#ifndef DEBUG
    printf("配合宏,有定义DEBUG");
#else
    printf("配合宏");
#endif
    printf("必须执行");
image.png

相关文章

网友评论

      本文标题:C

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