C环境

通过vscode Remote SSH 到linux远程服务器,使用linux gcc

 
环境说明:
购买一个云linux服务器,或者安装一个linux的虚拟机,
通过vscode remote ssh插件远程连接,完成一系列的C的语法学习
如果安装的是ubantu环境,自带桌面及C系列的软件包,推荐使用


vscode下载 
https://code.visualstudio.com/

安装Remote SSH插件
ctrl+shift+p 选择,remote ssh 

如果默认端口是22的话,ssh xt@357210.xyz
其他端口需要指定端口号
ssh -p 26759 xt@357210.xyz

对应配置文件的格式
Host 357210.xyz
    HostName 357210.xyz
    User xt
    Port 26759

ctrl+shift+p 选择,remote-ssh:connect to host
在弹出的框中输入密码,然后回车

 

    

 

    

C概述

    
在计算机的世界中,C语言是基础中的基础,C语言中讲述了高级编程中最基础的那部分知识,
高级语言就是除了汇编,编译原理,机器语言之外的,人方便阅读的语言

操作系统是C写的,未来会有其他语言冲击这个领域,目前C的地位还没有哪个语言能替代

在编程界,没有哪个语言能摆脱C语言的影响,它们大多都调用了C语言的库函数,
毕竟OS都是C写的,语言要运行在OS上,这也是没办法的事,

未来开发人员的三条路:
1. 向上发展,开发一系列方便好用的模块,打造一个好用的平台,调调API就解决了一切,这叫服务模式
2. 向下发展,底层建设,网络的底层研究透了不管是当黑客还是转身当杀毒专家都可以,OS底层,稀缺人才哪里都有需要
3. 算法方向,不限语言,但编程发展到这一步,会点C/C++对学习算法是有帮助的;学习AI,会C/C++也是不错的加分项

 

    

C运行示例:以ubantu环境为例


xt@ai:/opt/tpf/cwks/src/test$ cat a.c
#include <stdio.h>

int main(){
    printf("%d\n",1);
    return 0;
}

编辑运行


xt@ai:/opt/tpf/cwks/src/test$ gcc a.c 
xt@ai:/opt/tpf/cwks/src/test$ ls
a.c  a.out
xt@ai:/opt/tpf/cwks/src/test$ ./a.out 
1

    
printf:按指定的格式将数据输出到控制台,%d表示整数,\n表示换行,printf() 函数在 "stdio.h" 头文件中声明。
stdio.h 是一个头文件 (标准输入输出头文件) ,定义一系列io方法,printf就是其中一个
#include:预处理命令,引用外部写好的文件到本文件,从而本文件可以调用其中的方法,
return 0; 退出程序

/* ... */ 注释

 


 

  

 


C数据类型

C简单数据类型:数字,字符,布尔

 
数字:int,float,double 

字符:char 
其实,每个字符都对应一个整数
换句话讲,计算机中一切皆数字,将不同的数字,找个符号与之对应,就有了字符 

布尔:bool,
布尔就是逻辑真假类型,bool按发音直译过来的单词 

bool:0为假,其他皆为1(表示真)

 
#include <stdio.h>
#include <stdbool.h>

int main(void)
{
    bool flag = 11;
    printf("flag:%d\n", flag);
    return 0;
}

 
0为假,1为真,布尔值只有0与1两个值,占1个字节

bool默认值为0-假

 
#include <stdio.h>
#include <stdbool.h>

int main(void)
{
    bool flag;
    printf("flag:%d\n", flag);//flag:0

    return 0;
}

C变量

 
定下数据类型后,就有了变量,

变量是一个量,
- 量就是大小,多少,规模,量化后,就可以比一比大小 
- int a = 1; 1就是要表示的量 

变量可以表示 一类数据,
- 比如int类型数据,
- 用符号代表该类数据,1,2,3,...n这样的数据都可以使用一个变量表示 
- 这类数据都有相同的类型int 

变量,变量,量的值是可以变化的
- a = 2; a被声明后,可以放任何一个int数据 

有多少种数据类型,就有多少种变量 
    

变量主要有:数字,字符,指针

 
#include <stdio.h>

int main(){
    int a=1,b=2;
    char c = 'a';
    float f = 1.0;
    double d = 2.0;

    printf("a=%d\n",a);
    printf("c=%c\n",c);
    printf("f=%f\n",f);
    printf("d=%f\n",d);
    return 0;
}


xt@ai:/opt/tpf/cwks/src/test$ gcc a11.c 
xt@ai:/opt/tpf/cwks/src/test$ ./a.out 
a=1
c=a
f=1.000000
d=2.000000

变量占用字节大小:sizeof

 
#include <stdio.h> 
int main(){
    int a = 11;
    float b = 11;
    printf("a=%f,b=%f\n",a,b); // a=11.000000,b=0.000000
    printf("size int:%d,size float:%d\n",sizeof(a),sizeof(b)); // size int:4,size float:4
    return 0;
}

C常量

 
有多少种数据类型,就有多少种常量

常见常量:整数,浮点,布尔,字符,字符串

 
#include <stdio.h>

int main(){
    printf("数字常量:%d\n",1);
    printf("字符常量:%c\n",'c');
    printf("字符串常量:%s\n","abc");
    return 0;
}


xt@ai:/opt/tpf/cwks/src/test$ gcc a.c 
xt@ai:/opt/tpf/cwks/src/test$ ./a.out 
数字常量:1
字符常量:c
字符串常量:abc

define定义的常量只是字符串的替换

 
#include <cstdio>
#define Pi 3.14 
#define add + 
int main(){
    int a = 1 add 2;
    printf("a=%d\n",a);
    return 0;
}

$ gcc b.c 
$ ./a.out 
a=3

 
只是将 1 add 2 中的add替换为了+
所以,就成了 1 + 2 

define这种处理是在预编译阶段,
也就是说这里的Pi,add压根就不是什么变量,只是一种语法操作

从这个角度看,define定义常量没有多少性能的提升 

const 定义常量

 
#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
int main() {
    const int B = 2;
    int c =B;
    int d =B;

    printf("B addr:%p\n",&B);
    printf("c addr:%p\n",&c);
    printf("d addr:%p\n",&d);
    return 0;
}


$ gcc c.c 
$ ./a.out 
B addr:0x7ffff75393dc
c addr:0x7ffff75393d8
d addr:0x7ffff75393d4

顺更提一下,栈内存的地址是从高到低排列的 

 
static变量在“代码区,数据区,堆,栈”中的 数据区 中
const让变量不再变化,本质是还是变量,只是值不能被改变
想要它成为静态变量,还得加个static,然后它的存储位置直接就到数据区了

 
#include <stdio.h> 
int main(){
    static const int a = 1;
    printf("a=%d\n",a);
    return 0;
}

$ gcc b.c 
$ ./a.out 
a=1

define与const区别举例:variably modified 'arr1' at file scope

 
#include <stdio.h>
const int max_arr_len=101;
struct CsvRow
{
    char* arr1[max_arr_len];
};
int main ()
{
    return 0;
}

$ gcc d.c 
d.c:5:11: error: variably modified 'arr1' at file scope
     char* arr1[max_arr_len];
           ^~~~

下面代码是对了,那怎么上面的就错了?

 
#include <stdio.h> 
int main ()
{
    const int max_arr_len=101;
    char* arr1[max_arr_len];
    return 0;
}

核心在于const定义的常量,本质还是变量,只是它不能修改
struct中数组长度是要在编辑时确定下来的,而一个变量的值在运行时赋值的
main里面的代码可以运行,在于它的范围是方法内,是在运行时才确定

define 定义常量

 
#include <stdio.h> 
#define max_arr_len 101
struct CsvRow
{
    char* arr1[max_arr_len];
};
int main ()
{
    return 0;
}

C变量生命周期

 
linux一个进程大致分 
文本区:数据代码,只读
数据区:全局变量 及 静态变量 存储区域,程序编辑阶段就需要初始化的数据存放该区
堆栈区:局部变量/数据 存储区域,栈通常是int,float等方法中定长变量存储区域,堆是变长常量存储区域

c static

 
static int count = 0;
static 修饰变量/方法,会将该变量/方法限定于所在文件,并转到进程内存的数据区 

对于全局变量,外部程序无法访问其他文件中的被static修饰的变量/方法 
如果不想某个 函数/变量 被外部文本访问,加上static就可以了

对于方法中的局部变量,本应在方法调用之后消失,
但由于作用域被提升到了文件的级别,方法调用后也不会消失了

 
局部变量在堆栈中,但加上static后,会转移到数据区,
方法中的静态变量放在数据区固定的地址处,
所以 局部静态变量 的值并不会随方法结束而消失

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

void test(){
    static int count = 0;
    count++;
    printf("%d",count);
}
    
int main ()
{
    int i;
    for(i =0;i<3;i++){
        test();
    }
    return 0;
}

输出
123

c extern

 
c生成可执行文件主要分:编辑,连接,执行 
最终所有代码是在一个文件中了,
如果开发的代码多,除了include可以引入外部文件外,
还可以使用extern告诉编辑器,某个变量/方法在其他文件中,可以先用用

若不使用extern,那么所谓的全局变量,
也仅限于本文件内,虽然最后所有文件还是会连接到一起

extern 函数

 
extern double str_sec(char* t_str);

C static 示例1

方法内的静态变量举例:判断卡号是否不连续了

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

//是否首次出现,是否不连续了
bool is_first_show(char* card){
    static char* card_old = NULL;
    if(card_old==NULL){
        card_old = card;
        return 1;
    }else{
        if(strcmp(card,card_old)==0){
            card_old = card;
            return 0;
        }else{
            card_old = card;
            return 1;
        }
    }
    
}

int main(void)
{
    char* card="623004134003432038";
    bool flag = is_first_show(card);
    if(flag){
        printf("1 首次出现  \n");
    }

    card="623004134003432031";
    flag = is_first_show(card);
    if(flag){
        printf("2 首次出现  \n");
    }

    flag = is_first_show(card);
    if(flag){
        printf("3 首次出现 \n");
    }

    card="623004134003432038";
    flag = is_first_show(card);
    if(flag){
        printf("4 首次出现  \n");
    }

    return 0;
}

 
$ gcc d.c 
$ ./a.out 
1 首次出现  
2 首次出现  
4 首次出现  

上面的代码有问题,正确的如下

 
上面的代码问题在于,使用的是指针,指针变量可能会出现,地址相同,而内容已经变了的情况

card_old = card; 这行代码只是改变了指针变量的地址,与比较内容是否相等还是有细微差异的
将card的地址赋予card_old,如果card是数组,在方法外部被修改内容,
那么card_old与card同地址,其内容也随之改变,这与比较内容是否相同的本意不符 

优化后的代码如下 
这个地方也可以当作一个指针使用的典型例子

 
//是否首次出现,是否不连续了
bool is_first_show(char* card){
    static char card_old[30];
    int count = strlen(card)+1;
    if(card_old==NULL){
        memcpy(card_old,card,count);
        return 1;
    }else{
        if(strcmp(card,card_old)==0){//相同时不必再赋值了
            return 0;
        }else{
            memcpy(card_old,card,count);
            return 1;
        }
    }
}

隐式转换

 
隐式转换的顺序:
int -> unsigned int -> long -> unsigned long -> unsigned long long -> float -> double -> long double

#include <stdio.h>

int main(){
    int a=2;

    float f;
    f = a;

    double d;
    d =f ;

    printf("a=%d\n",a);  # a=2
    printf("f=%f\n",f);  # f=2.000000
    printf("d=%f\n",d);  # d=2.000000
    return 0;
}

显式转换

 
#include <stdio.h>

int main(){
    int a;
    float f=1;
    a = (int)f;

    printf("a=%d\n",a);  // a=1
    printf("f=%f\n",f);  // f=1.000000
    return 0;
}

计算机内存


计算机硬件的组成之一是内存,通常是内存条
OS是软件,由 编程语言 编写,
这里说的是内存是OS的内存,它将电脑中多个内存条统一编码,
每8位一个字节,标记为一个地址,并且从0编号,形成一个虚拟内存
我们说一个电脑内存多大指是的虚拟内存,可能有多个内存条映射而成

C指针定义


电脑的虚拟内存,每个字节标记一个地址,从0编号,
所以说,地址是一串数字,或者说是一个整数,

它与内存的一个字节对应(确切是字节的起始位)
这个关联内存位置的数字,就是指针,它指向内存的一个位置,一个字节的开头,
到哪里结束,即指针确定数据所占的字节数,由指针的数据类型决定

由于是整数,指针可以有加减运算,也可以移动, 类似刻度尺上的数字 
可以通过指针 访问/操作 内存

取地址指: 取 从指定地址开始,以数据类型所占字节数为长度的数据

 
指针是一个地址,是一个特殊意义的数字

指针变量是一个变量,专门存储地址的变量,所有变量的地址长度一致,
不管是什么指针变量,它们所占的字节数一致,因此它们的值都是地址 
如果是64位计算机,通常是8个字节

指针变量的值是一个8字节(64位)的数字地址,所有指针变量都是如此

 
#include <stdio.h>

int main(){
    int* a;
    printf("a adder size of byte:%ld\n",sizeof(a));
    return 0;
}
    
$ gcc g.c 
$ ./a.out 
a adder size of byte:8
  

 
#include <stdio.h>   

// 定义枚举类型  
enum AVMediaType {  
    AVMEDIA_TYPE_UNKNOWN = -1,  ///< 通常被视为 AVMEDIA_TYPE_DATA  
    AVMEDIA_TYPE_VIDEO,  
    AVMEDIA_TYPE_AUDIO,  
    AVMEDIA_TYPE_DATA,          ///< 不透明数据,通常连续  
    AVMEDIA_TYPE_SUBTITLE,  
    AVMEDIA_TYPE_ATTACHMENT,    ///< 不透明数据,通常稀疏  
    AVMEDIA_TYPE_NB  
};  
    
// 一个函数,用于打印枚举类型的名称  
void printMediaType(enum AVMediaType type) {  
    switch (type) {  
        case AVMEDIA_TYPE_UNKNOWN:  
            printf("AVMEDIA_TYPE_UNKNOWN\n");  
            break;  
        case AVMEDIA_TYPE_VIDEO:  
            printf("AVMEDIA_TYPE_VIDEO\n");  
            break;  
        case AVMEDIA_TYPE_AUDIO:  
            printf("AVMEDIA_TYPE_AUDIO\n");  
            break;  
        case AVMEDIA_TYPE_DATA:  
            printf("AVMEDIA_TYPE_DATA\n");  
            break;  
        case AVMEDIA_TYPE_SUBTITLE:  
            printf("AVMEDIA_TYPE_SUBTITLE\n");  
            break;  
        case AVMEDIA_TYPE_ATTACHMENT:  
            printf("AVMEDIA_TYPE_ATTACHMENT\n");  
            break;  
        case AVMEDIA_TYPE_NB:  
            printf("AVMEDIA_TYPE_NB\n");  
            break;  
        default:  
            printf("Unknown media type\n");  
            break;  
    }  
}  
    
int main() {  
    
    // 示例使用枚举类型  
    enum AVMediaType mediaType1 = AVMEDIA_TYPE_VIDEO;  
    enum AVMediaType mediaType2 = AVMEDIA_TYPE_AUDIO;  
    enum AVMediaType mediaType3 = AVMEDIA_TYPE_UNKNOWN;  
    
    printf("c enum:%d\n",AVMEDIA_TYPE_VIDEO);
    // 打印枚举类型的名称  
    printMediaType(mediaType1);  
    printMediaType(mediaType2);  
    printMediaType(mediaType3);  
    
    return 0;  
}

 
c enum:0
AVMEDIA_TYPE_VIDEO
AVMEDIA_TYPE_AUDIO
AVMEDIA_TYPE_UNKNOWN

虽然本质是数字,但却不能直接赋值给int类型

 


C运算

C格式化输出:printf


#include <stdio.h>
int main(){
    int a,b=2;
    char c;
    float f;
    double d;
    scanf("%d %c %f %lf",&a,&c,&f,&d);
    printf("a=%d\n",a);
    printf("c=%c\n",c);
    printf("f=%f\n",f);
    printf("d=%f\n",d);

    scanf("%d,%c,%f,%lf",&a,&c,&f,&d);
    printf("a=%d\n",a);
    printf("c=%c\n",c);
    printf("f=%f\n",f);
    printf("d=%f\n",d);
    return 0;
}

C运算符

逻辑运算符:&&,||,!

 
#include <stdio.h> 
#include <stdbool.h> 

int main()
{
    int a =1,b=2,c=3;
    bool f = 0;
    if((a>0 && b>1 && c>2) || !f){
        printf("ok\n");
    }
    return 0;
}     

位运算符:&,|,^,~,<<,>>

 


C进制

二进制:0b/0B,0-1

 
#include <stdio.h> 

#define u32 unsigned int
#define u8 unsigned char

//输出二进制
void print_bin(u32 input)
{
    u8 temp[33] = {0};  
    int i = 0;
    while(input)
    {
        temp[i] = input % 2;	
        input = (u32)input / 2;  
        i++;  
    }
    for(i--; i>=0; i--)  
    {
        printf("%d",temp[i]);
    }
    printf("\r\n");
}

int main()
{
    print_bin(5);//101
    int a =0b101,b=0b011;
    print_bin(a&b);//1
    print_bin(a|b);//111
    print_bin(a^b);//110
    print_bin(~b);//1111 1111 1111 1111 1111 1111 1111 1100
    print_bin(b<<1);//110 都向左移动一位,相当于每1位都乘以进制2,(1*2 + 1*1)*2 = 6 
    print_bin(b>>1);//1 
    return 0;
}
        

八进制:0-7,以数字0开头

 
#include <stdio.h> 
int main()
{
    int a =0101,b=0011;
    printf("10进制 a=%d,b=%d\n",a,b);
    printf("8进制 a=%o,b=%o\n",a,b);
    return 0;
}

 
$ ./a.out 
10进制 a=65,b=9
8进制 a=101,b=11

16进制:0-9,A-F或a-f不区分大小写,以0x开头

 
#include <stdio.h> 
int main()
{
    int a =0x101,b=0x011;
    printf("10进制 a=%d,b=%d\n",a,b);
    printf("16进制 a=%x,b=%x\n",a,b);
    return 0;
}
        

 
$ gcc num.c 
$ ./a.out 
10进制 a=257,b=17
16进制 a=101,b=11

C数字处理

 
4字节,32位
int最小值=-2147483648, int最大值=2147483647

 
#include <stdio.h> 
#include <limits.h> 
#include <float.h> 

int main()
{
    printf("int类型数据所占空间=%d\n", sizeof(int));
    printf("int最小值=%d, int最大值=%d\n", INT_MIN, INT_MAX);	// 使用limits.h里的宏

    //方法2
    signed int max = (1 << (sizeof(int) * 8 - 1)) - 1;	// 位计算
    signed int min = -(1 << (sizeof(int) * 8 - 1));
    printf("int最小值=%d, int最大值=%d\n", min, max);

    // 方法3
    printf("int最小值=%d, int最大值=%d\n", max+1, min-1);

    // double数据类型精度及范围--数据很大,1.79769e+308
    printf("float类型数据所占空间:%d\n", sizeof(float));
    printf("double类型数据所占空间:%d\n", sizeof(double));
    printf("double精度:%lf\n");	//有警告,不过可以运行
    printf("double最小值=%lf\n, double最大值=%lf\n", -DBL_MAX, DBL_MAX);	//使用float.h的宏

    //试一试它是不是真的这么大
    double max_double = 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000;
    printf("double max value is %lf\n",max_double);
    
    return 0;
}
        

int是2为开头的10位数

 
$ ./a.out
int类型数据所占空间=4
int最小值=-2147483648, int最大值=2147483647
int最小值=-2147483648, int最大值=2147483647
int最小值=-2147483648, int最大值=2147483647
float类型数据所占空间:4
double类型数据所占空间:8
double精度:0.000000
double最小值=-179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000
, double最大值=179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000
double max value is 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000

double1.79769e+308,300多位,平时用足够,不需要再加什么long, 已经够long了

 
179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000

 

    

 
float可以很大,最大为3.4乘以10的38次方(double是1.7×10的308次方)
但它的有效数字位数最大为7,超过7的部分,经printf("%f")格式化输出后 
- 小数点部分会被置为0 
- 大于0但超过7位的部分就不准了
    

float有效小数位:小数点后6位

 
#include <stdio.h> 

int main(){
    float a = 0.1234567;
    printf("%f\n",a);  // 0.123457
    printf("size of float :%ld\n",sizeof(a)); //size of float :4
    return 0;
}

如果小数点后超出6位,直接四舍五入
        

c float的精度

 
#include <stdio.h>
int main(){
    float a = 1234567890.1234567;
    printf("a = %f\n", a);  // a = 1234567936.000000
    return 0;
}

float小数点后6位有效小数,但它的有效位数是7,第8位就开始不准了

float的存储结构

 
float类型数据占四个字节,一个字节是8位,共32位
以类似 3.402823466 E+38 的格式存储

第1位:  0表示正数,1表示负数
第2-9位:指数
第10-32:几点几的小数部分

AI中的小数

 
import numpy as np
np.allclose(1e-8,0)  # True 

AI中认为小数点高于7位的部分为0,
而C中float高于6位就自动四舍五入
第8位就是0,第7位四舍五入到第6位上,即合理又不浪费
还减少了计算的代价,

AI框架最终还是要转化为C的float类型进行计算,
而double的有效位数为15,对AI来说太多了,
所以AI中数据类型通常指定为float32 

float的最大值 :3.402823466 E+38

 
3.4乘以10的38次方是一个非常大的数
10的9次方为亿,其38次方不知道多少个亿
大部分运算使用float类型够用 

 

    

 
#include <stdio.h>

int main(){
    double a = 12345678901234567890.1234567;
    printf("%lf\n",a);  // 0.123457
    printf("size of float :%ld\n",sizeof(a)); //size of float :4
    return 0;
}

double的有效位数为17,超出17位的就不准了

 
gcc db.c 
./a.out 
12345678901234567168.000000
size of float :8

double1.79769e+308,300多位,平时用足够,不需要再加什么long, 已经够long了

 
179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000

C数字补0

 
#include <stdio.h>
int main(){
    int a = 111;
    printf("%05d\n",a); 

    return 0;
}

输出
00111

 

  

sprintf格式化输入

 
#include <stdio.h>
#include <stdlib.h>
#define N2 19 

int main(){
    int a = 111;
    char str[N2];
    sprintf(str,"%05d",a);
    printf("%s\n",str);
    return 0;
}

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

void ifmt(char** res,int i){
    char tmp[100];
    sprintf(tmp,"%d",i);
    *res = (char*)malloc(strlen(tmp)*sizeof(char)+1);
    sprintf(*res,"%s",tmp);
}

void dfmt(char** res,double i){
    char tmp[100];
    sprintf(tmp,"%lf",i);
    *res = (char*)malloc(strlen(tmp)*sizeof(char)+1);
    sprintf(*res,"%s",tmp);
}



int main() {
    char* res;
    int a =3;
    ifmt(&res,a);
    printf("-%s-\n", res);
    free(res);

    double b = 3.2;
    dfmt(&res,b);
    printf("-%s-\n", res); //-3.200000- 多了一些0
    free(res);

    return 0;
}
        

 


sscanf int

 
#include <stdio.h> 
int main(){
    char* ss = "123";
    int i ;
    sscanf(ss,"%d",&i);
    printf("d=%d\n",i);
}

$ gcc b.c 
$ ./a.out 
d=123

sscanf double

 
#include <stdio.h> 
int main(){
    char* ss = "123.4";
    double i ;
    sscanf(ss,"%lf",&i);
    printf("d=%lf\n",i);
}

$ gcc b.c 
$ ./a.out 
d=123.400000

atoi:只针对整数,double会损失小数部分

 
#include <stdio.h> 
int main(){
    char* s1 = "123";
    int i = atoi(s1);
    printf("i=%d\n",i); //i=123

    char s2[] = "123.456";
    double d = atoi(s2);
    printf("d=%lf\n",d); //d=123.000000
}

 


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

void tobin(int n){
    int arr[32] = { 0 };//int 型有32位,
    int i = 0;
    while (n > 0)
    {
        arr[i] = n % 2;
        ++i;
        n /= 2;
    }
    int k = 0;
    for (k = i-1; k >= 0; k--) //逆序输出
    {
            printf("%d", arr[k]);
    }
}

int main()
{
    tobin(5);
    return 0;
}
        

 
...........................................................

C循环控制

for循环

 
# include <stdio.h> 
int main()
{
    int i = 1;
    int sum = 0;
    for (i=0; i<=3; ++i)
    {
        sum = sum + i;
    }
    printf("sum = %d\n", sum);
    return 0;
}

 
for (表达式1; 表达式2; 表达式3)
表达式1可以空着,
但不能写成变量的定义,即不能写int i =0;
int i的定义必须写在for的前面,
i = 0的赋值这样的表达式才算正确

C 断言

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

typedef char string[30];

struct Students
{
    int    student_id;  // 4 bytes 
    string student_name;// 32 bytes
    float score;       // 4 bytes
};

struct Students st1;

void test(int a){
    // 动态断言 
    // 条件成立则顺利通过,不成立则抛出异常并中断程序;
    assert(a>5);

    //静态断言,用于编译检测
    static_assert(sizeof(st1)>36,"编译检查失败,长度必须大于36");
}

void test_exit(int a){
    if (a<3){
        exit(-1);
    }
}

int main(){
    int a = 6;
    test_exit(a);
    test(a);
    return 0;
}


#include <stdio.h>
int main(){
    int a,b=2;
    char c;
    float f;
    double d;
    scanf("%d %c %f %lf",&a,&c,&f,&d);
    printf("a=%d\n",a);
    printf("c=%c\n",c);
    printf("f=%f\n",f);
    printf("d=%f\n",d);

    scanf("%d,%c,%f,%lf",&a,&c,&f,&d);
    printf("a=%d\n",a);
    printf("c=%c\n",c);
    printf("f=%f\n",f);
    printf("d=%f\n",d);
    return 0;
}

格式在C中如果写错了,后果不可预料

 
#include <stdio.h> 
int main(){
    int a = 11;
    float b = 11;
    printf("a=%d,b=%f\n",a,b); // a=11,b=11.000000
    printf("size int:%d,size float:%d\n",sizeof(a),sizeof(b)); // size int:4,size float:4
    return 0;
}

注意看,b并没有写错,错的是a,但结果却是b输出不正确

 
#include <stdio.h> 
int main(){
    int a = 11;
    float b = 11;
    printf("a=%f,b=%f\n",a,b); // a=11.000000,b=0.000000
    printf("size int:%d,size float:%d\n",sizeof(a),sizeof(b)); // size int:4,size float:4
    return 0;
}

 



scanf为格式化输入,将常量存放到指定的地址&
printf为格式化输出,将常量从内存输出到控制台


xt@ai:/opt/tpf/cwks/src/test$ gcc a12.c 
xt@ai:/opt/tpf/cwks/src/test$ ./a.out 
1
2
2.0
3.0
a=1
c=2
f=2.000000
d=3.000000
1,a,1.0,3
a=1
c=a
f=1.000000
d=3.000000 

 

  

puts

 
#include <stdio.h>
int main()
{
    char   c;
    puts("请输入一个字符");

    c = getchar(); 

    puts("你输入的字符是");
    putchar(c); 
    return 0;
}

printf,格式化输出,可输出各种格式
puts,只输出字符串,并在结尾带个换行符

 

    
C 结构体

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

typedef struct Kong
{

} K;

typedef struct Kis
{
    int a;
} Ki;
    
typedef struct Students
{
    int   sid;
    char  name[150];
} Student;
    
int main( )
{
    K kk;
    Ki ki;
    printf("before init K struct, the count of the Bytes:%ld\n",sizeof(kk));
    printf("before init Ki struct, the count of the Bytes:%ld\n",sizeof(ki));


    Student st;
    printf("before init Students struct, the count of the Bytes:%ld\n",sizeof(st));

    st.sid = 12345;
    strcpy(st.name, "东段洋");

    Student st2;
    memcpy(&st2, &st, sizeof(st));

    printf( "sid : %d\n", st2.sid);    
    printf( "name : %s\n", st2.name);

    return 0;
}

 
before init K struct, the count of the Bytes:0
before init Ki struct, the count of the Bytes:4
before init Students struct, the count of the Bytes:156
sid : 12345
name : 东段洋
    

多于一个字段时,struct多出两个字节,用途尚不知

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

typedef struct Kis
{
    int a;
    int b;
    char  name[150];
} Ki;
    
typedef struct Students
{
    int   sid;
    char  name[150];
} Student;
    
int main( )
{
    Ki ki;
    printf("before init Ki struct, the count of the Bytes:%ld\n",sizeof(ki));

    Student st;
    printf("before init Students struct, the count of the Bytes:%ld\n",sizeof(st));
    return 0;
}
    

 
before init Ki struct, the count of the Bytes:160
before init Students struct, the count of the Bytes:156    

结构体COPY:复制与引用的区别


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

typedef char string[30];
    
struct Students
{
    int    student_id;
    string student_name;
    float score;
};
    
/* 函数声明 */
void copy( struct Students st );
void update( struct Students *st );

int main( )
{
    struct Students st1;        /* 声明 */
    st1.student_id = 11;
    
    copy( st1 );
    printf("name:%s\n",st1.student_name);
    printf("score:%f\n",st1.score);

    update( &st1 );
    printf("name:%s\n",st1.student_name);
    printf("score:%f\n",st1.score);
    return 0;
}


void copy( struct Students st )
{
    strcpy( st.student_name, "七三学徒"); 
    st.score = 60;
}

void update( struct Students *st )
{
    strcpy( st->student_name, "七三学徒"); 
    st->score = 60;
}

输出
name:
score:0.000000
name:七三学徒
score:60.000000

结构体指针 类型

 
#include <stdio.h> 
struct Empty{

};

//结点结构体
struct Node{
    int data;             //数据域
    struct Node* next;     //指针域
};

//链式队列结构体
typedef struct LQ{
    int size;            //队列大小
    struct Node out;     //头结点,本身data不存储数据,仅使用其next,其next永远指向队列的第一个节点
    struct Node* in;     //尾结点指针,指向队列最后一个节点,其数据就是队列最后一个节点的数据 
    
}LkQueue, *LinkQueue;    //定义两个类型,一个LQ结构体,一个LQ结构体指针


int main (int argc, char** argv)
{
    
    printf("Empty size :%ld\n",sizeof(struct Empty));// 1
    printf("node size :%ld\n",sizeof(struct Node));// 16 
    printf("LkQueue size :%ld\n",sizeof(LkQueue));// 32

    printf("LinkQueue:%ld\n",sizeof(LinkQueue));//8

    int a =2;
    int* b = &a; 
    printf("int size:%ld\n",sizeof(a));//4
    printf("int* size:%ld\n",sizeof(b));//8

    return 0;
}


C函数

C函数作为参数

ElemType(*FunctionName)(ElemType, ElemType, ......)

 
#include <stdio.h> 
int add(int a,int b){
    return a+b;
}
void test(int (*func)(int,int),int a,int b){
    int res = func(a,b);
    printf("res=%d\n",res);
}
int main()
{
    test(add,1,2);
    return 0;
}

(base) [xt@ai1 C++]$ gcc f.c 
(base) [xt@ai1 C++]$ ./a.out 
res=3

C命令行参数

 
#include <stdio.h>
int main( int argc, char *argv[] )  
{
    int i;
    for (i=0;i<argc;i++){
        printf("第%d个参数:%s\n",i,argv[i]);
    }
}

 
$ gcc a.c
$ ./a.out -name aaa
第0个参数:./a.out
第1个参数:-name
第2个参数:aaa

 
int main (int argc, char** argv)
{
    
    return 0;
}
    

C可变参数

param_count参数的位置必须在...的前面

 
#include <stdarg.h>
#include <stdio.h>
    
void parse_params(int param_count, ...);
    
int main(void)
{
    // 超出param_count的被忽略 
    char *p1 = "aa";
    char *p2 = "bb22";
    char *p3 = "cc";
    parse_params(2, p1, p2, p3);
}

void parse_params(int param_count, ...)
{
    
    va_list param_list;
    va_start(param_list, param_count);
    
    int idx;
    char *val;
    printf("---------------\n");
    for(idx = 1; idx <= param_count; ++idx){
        val = va_arg(param_list, char*);
        printf("第 %d 个参数: %s\n", idx, val);
    }
    printf("---------------\n");
    va_end(param_list);
}

可变参数2

 
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
    
void parse_params(int param_count, ...);

typedef char string[30];
struct Data
{
    int    id;
    float score;
    string name1;
    string name2;
    string name3;
};

int main(void)
{
    struct Data d1;
    d1.id = 1;
    d1.score = 2;
    strcpy(d1.name1,"aa");

    struct Data *p1 = &d1;
    parse_params(1, p1);
}
    
void parse_params(int param_count, ...)
{
    va_list param_list;
    va_start(param_list, param_count);
    
    int idx;
    struct Data *val;
    printf("---------------\n");
    for(idx = 1; idx <= param_count; ++idx){
        val = va_arg(param_list, struct Data*);
        printf("第 %d 个参数: %s\n", idx, val->name1);
    }
    printf("---------------\n");
    va_end(param_list);
}

自定义可变参数:使用结构体封装参数,以复制的方式传递,内外操作互不影响

 
#include <stdio.h>
#include <string.h>
    
typedef char string[300];

struct Params
{
    int    int_count;
    int float_count;
    int string_count;
    int int_val[100];
    float float_val[100];
    string string_value[100];
};

void parse_params(struct Params params);

int main(void)
{
    struct Params d1;
    d1.int_count = 1;
    d1.int_val[0] = 10;

    d1.string_count = 2;
    strcpy(d1.string_value[0],"aa");
    strcpy(d1.string_value[1],"bbb");

    parse_params(d1);
}
    
void parse_params(struct Params params)
{
    printf("---------------\n");
    int idx;
    for(idx = 1; idx <= params.int_count; ++idx){
        printf("第 %d int个参数: %d\n", idx, params.int_val[idx-1]);
    }

    for(idx = 1; idx <= params.string_count; ++idx){
        printf("第 %d string个参数: %s\n", idx, params.string_value[idx-1]);
    }
    printf("---------------\n");
}

 

  

 

    
C与C++区别

 
.c    C语言 
.cpp  C++ 

通常gcc编辑.c文件
也可以使用gcc去编译了一个.cpp文件,但编辑的细节是不一样的
比如&符号在C语言中是取地址符,在C++中还可以是引用传址,只要是.cpp后缀,&符号就会被看作是C++的引用传值  
而C语言的中传址使用的是指针* 

 
gcc m.cpp 
gcc 运行了cpp程序,并且可以使用C++的引用,
但如果不安装g++则会报下面的错误
$ gcc m.cpp
gcc: fatal error: cannot execute ‘cc1plus’: execvp: No such file or directory

安装g++后就可以了 
$ sudo apt install g++
$ gcc m.cpp
$

 

    

 

    

 
&符号在C语言中是取地址符,

在C++中还可以是引用传址,

只要是.cpp后缀,&符号就会被看作是C++的引用传值

而C语言的中传址使用的是指针* 

 

    

 

    

 


 

  

 


参考文章
    C语言可变参数