语言篇|现代C记录—简略篇


highlight: agate
theme: cyanosis

一.前言

导图
image.png

  • 本文主要对上一篇文章的精简归纳,主要分上面三个大部分
  • 进阶篇暂不包括线程、进程、网络篇,计划单独写

上一篇传送
语言篇—现代C记录 - 掘金

学而时习之,不亦说乎

二.基本篇

2.1 HelloWorld

基本结构: 指令、函数、语句、显示、注释

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

编译输出:gcc/cc -o <outputfile> <sourcefile> [-w]

$ gcc hello.c  
  
$ ./a.out  
Hello, World!

2.2 类型

image.png

2.2.1 基本类型

数值&字符
基本类型表述一般读写格式
short、int、long、long long有符号整数,最左边位为0代表正数,1代表负数%<h\l><d\o\u\x>
在上面前加unsigned无符号整数
float单精度浮点数%f , %g , %e
double双精度浮点数%f , %g , %e 前加小l
long double扩展浮点数%f , %g , %e 前加大L
char字符类型%c

2.2.2 其他类型

数组
#define N 10
#define M 11
int main(){
    /**
     * 1.一维数组
     * 定义:int a[N];
     * 初始化:int a[10]={1,2,3,4}
     *        或者 a[10]={0}
     * c99: int a[10]={[1]=9,[5]=7};指定位置初始化
     * 同时存在 int a[]={1,2,9,4,[0]=5,8} 输出 {5,8,9,4}
     */
 
    /**
     * 2.多维数组
     * 定义:int a[M][N];
     * 初始化:int a[M][N] = {{1,2,3},{1,23,4},...};可不全列出
     * C99(可指定位置初始化): int a[2][2] = { [0][1]=1,[1][1]=2};
     */
     
   /**
    * 3. 常量数组 constant arrays
    * 定义:在数组前面加 const
    */
  const char hex_chars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
  

  /**
   ** 4. c99中的变长数组VLA
   * 变长长度不一定用变量来指定,任意表达式(可以包含运算):
   * int a[3*i+5];
   * int b[j+k];
   * 变长数组主要限制是没有静态存储期限,没有初化式
   */
   int vla[3*N+M];
 }
字符串
char *p_str = "hello world!";\\指针式
char arry_str[] = "hello world!"; \\数组式
结构、联合、枚举
#include <stdio.h>

/*-----------------------结构----------------------------*/
//省略了结构标记
struct {
  int version_1;
  char name_1;
} a1;

//(结构)定义方式1:包含了结构标记
struct Part {
  int version_1;
  char name_1;
} b1, b2, b3;//同时声明3个结构体变量,也可不在此处声明
struct Part b4;//声明一变量

//(结构)定义方式2:使用typedef
typedef struct {
  int version_2;
  char name_2;
} Part2;//此处使用类型定义将结构命名为类型Part2
Part2 c1, c2;//声明两个变量

/*-----------------------联合----------------------------*/
//(联合)定义方式1:包含了联合标记
union Union_1 {
  int weight;
  double name;
} d1, d2, d3;//同时声明3个联合变量,也可不在此处声明

//(联合)定义方式2:使用typedef
typedef union {
  int version_2;
  char name_2;
} Union_2;//此处使用类型定义将联合命名为类型Part2,

/*-----------------------枚举----------------------------*/
enum { ONE, TWO, THR } f1;//定义3个枚举常量 和一个变量
enum Enum_1 { FIV, SIX } f2;// 方式一:包含了枚举标记
typedef enum { TRUE=1, FALSE=0  } BOOL;//方式2:使用typedef类型定义

2.3 运算符与表达式

image.png

运算符

优先级名称符号结合性
1数组取下标[]左结合性
1函数调用()左结合性
1取结构和联合的成员. 右箭头->左结合性
1自增(后级)i++左结合性
1自减(后缀)i–左结合性
2自增(前缀)++i右结合性
2自减(前缀)–i右结合性
2取地址&右结合性
2间接寻址*右结合性
2一元正号+右结合性
2一元负号-右结合性
2按位求反~右结合性
2逻辑非!右结合性
2计算所需空间sizeof右结合性
3强制类型转换()右结合性
4乘法类运算符* / %左结合性
5加法类运算符+ -左结合性
6移位<< >>左结合性
7关系< > <= >=左结合性
8判等== !=左结合性
9按位与&左结合性
10按位异或^左结合性
11按位或|左结合性
12逻辑与&&左结合性
13逻辑或||左结合性
14条件? :左结合性
15赋值= *= /= %= += -= <<= >>= &= ^= |=右结核性
16逗号,左结合性

表达式

主要包含逻辑、算术、赋值表达式,前两种参考上方表格,赋值表达式注意一下复合赋值


  int android, java, python,c;
   /*简单赋值*/
  android = 1;
  java = android;
  c = android + java * 10;
  
  /*复合赋值*/
  c = android = python = java * 10;

控制流程

if,switch,while,for,break,continue,goto,参考上一篇。此处额外列举一下longjump

#include <setjmp.h>  
  
void callee(jmp_buf env) {  
  longjmp(env, 1);  
  /* unreachable */  
}  
  
void caller() {  
  jmp_buf env;  
  
  switch (setjmp(env)) {  
  case 0:  
    callee(env);  
    /* unreachable */  
    break;  
  case 1:  
    printf("returned from callee\n");  
    break;  
  default:  
    printf("unexpected setjump value\n");  
  }  
}

三. 进阶篇

3.1 指针

image.png

定义、赋值、取址&、间接寻址*

int main(){
    int i=0,*p;//声明指针变量p
    p=&i;// & 取址运算符,把i的地址赋值给了指针p
    int k=1,*q=&k;//合并写法
    printf("%d",*p);// *(间接寻址运算符)访问存储在对象中的内容 ,也可想象成&的逆运算
}

作为返回值

int *find_middle(int n, int a[n]) {
  return &a[n / 2];
}

作为参数

重要作用

  • 函数调用中用作实际参数的变量无法改变,当希望函数能够改变变量时,
    指针作为参数就能解决此问题。
  • 指针传参高效原因是,如果变量需要大量存储空间,传递变量的值有时会浪费时间和空间。
void decompose(double x, long *int_part, double *frac_part) {
  *int_part = (long) x;
  *frac_part = x - *int_part;
}

算术运算

int main() {
  int a[] = {1, 2, 3, 5, 6, 7, 8, 9, 10};
  int *p = &a[0], *q = &a[6];
  //1. 加减整数
  p += 4;
  p -= 3;
  
  //2. 指针相减(相减为距离)
  printf("q-p = %lld\n", q - p);

  //3. 指针比较(取决于在数组中的位置)
  printf("q>p:%d q<p:%d\n", q > p, q < p);

  //4. 复合常量的指针
  int *k = (int[]) {2, 4, 6, 8, 10};
  k += 4;
  printf("*K=%d\n", *k);

  return 0;

}

数组名作为指针指针作为数组名

#define N 10
/* 1 .用数组名作为指针*/
void case1() {
  int a[] = {1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, *p,*q;
  // 原始写法
  for (q = &a[0]; q < &a[N]; q++) {
      //省略
  }
  // 惯用法(更简洁)
  for (p = a; p < a + N; p++) {//此处a+N理解为,将a当作指针,然后做了指针的加法运算
  }
}

/* 2. 用指针作为数组名*/
void case1() {
  int a[] = {1, 2, 3, 5}, *p = a, *q = &a[0], *k;
  k = a;
  // 三种效果相同
  for (int i = 0; i < N; ++i) {
   printf("p[%d]=%d\n", i, p[i]);
   printf("q[%d]=%d\n", i, q[i]);
   printf("k[%d]=%d\n", i, k[i]);
  }
}

数组指针指针数组

int (*p)[n];//数组指针
int *p[n];//指针数组

image.png

指针高级应用malloc、calloc、realloc

动态存储分配&空判断&释放

三种内存分配函数(内存块都来至于堆区)描述原型
malloc分配内存块,但不进行初始化,少一步比calloc更高效void *malloc(size_t size)
calloc分配内存块,并进行清零void *calloc(size_t nmemb,size_t size)
realloc调整先前分配的内存块void *realloc(void *ptr,size_t size)
内存释放函数描述原型
free释放不需要的内存块void *free(void *ptr)
空指针
NULL如分配内存时未找到足够大小,就会返回空指针,if(P=NULL){...}
悬空指针如在释放p指向的内存后,再访问或修改释放掉的内存块
其他
->运算符称为右箭头选择,用于 node->value替代 *node.value的组合
restrict受限指针int *restrict p;
1.如果p指向的对象需要修改,则对象不会允许除p之外的任何方式访问
2.如果一个对象有多种访问方式,通常将这些方式互称为别名

3.2 预处理器

3.2.1 预处理器原理

image.png

gcc <SourceFile> -E 可以看到预处理器的输出

注意: 预处理器仅知道少量C语言的规则。因此在执行指令时是有可能产生非法程序,有时看起来正常但错误找起来难,可以检查一下预处理输出是一种方式

3.2.2 预处理指令

特征:

  1. 指令都以#开始
  2. 指令的符号间可以插入任意数量空格或水平制表符
  3. 指令总在第一个换行符出结束,除非使用 \ 符加入当前行末尾,明确地指明要延续
  4. 指令可以出现在程序的任何地方
  5. 注释可以和指令放在同一行

部分预处理指令:

预处理指令范畴指令
宏定义#define 指令定义一个宏
#undef指令删除一个宏
条件编译#if#ifdef#ifndef#elif#else#endif 以测试条件来确定程序是否包含一段文本块
文件包含#include 指定的文件内容被包含到程序中
其他特殊指令1. #error显示出错消息
2. #line 改变程序行编号方式
3. #pragam 为编译器执行某些特殊操作特供一种方法

3.2.3 宏定义

标题描述例子
简单宏#define 标识符 替换列表
参数宏#define 标识符(x1,x2,…,XN) 替换列表
#运算符将宏的一个参数字符串化,只允许出现在参数宏的替换列表
##运算符将两个记号(如标识符)粘合在一起,变成一个记号
预定义宏一种已经定义好的宏,每一个都是整数常量或字符串字面量__DATE__,__TIME__,__STDC__,__FILE__,__LINE__
空宏参数允许宏调用时任意参数为空,主要出现在参数宏或#运算符或##运算符的调用中
参数个数可变宏在宏定义的参数列表最后中使用...,…省略号在,__VA_ARGS__专用标识符,代表与...对应的参数

特殊__func__标识符与预处理器无关,相当于当前执行函数都的函数名字符串变量

#include <stdio.h>

/*
 * 1. 简单宏
 */
#define N 10
#define D "=%d\n"

/*
 * 2.参数宏
 */
#define IS_EVEN(n) ((n)%2==0)


/*
 * 3. #运算符,用于参数宏的替换列表中,字符串化
 */
#define P_INT(x) printf(#x D,x)

/*
 * 4. ##运算符,粘合两个记号,将两个记号变为一个记号
 */
#define M_K(n) i##n
#define JOIN(x,y,z) x##y##z



int main(){
IS_EVEN(3);
int a=3,b=2;
P_INT(a-b);
int M_K(1)=1,M_K(2)=2;// 相当于声明i1,i2

P_INT(i1-i2);

/*
 * 5. 预定义宏,整数常量或字符串字面量
 */
  puts(__DATE__);
  puts(__TIME__);
  printf("%d",__STDC__);
  printf("%s %d",__FILE__,__LINE__);

  /*
  * 6. 空宏参数
  */
  int M_K()=0;
  P_INT(i);
  int JOIN(r,,),JOIN(r,s,t),JOIN(r,,t);
  r=1,rst=2,rt=3;

  /*
   * 7 参数个数可变的宏,...省略号在参数列表最后,__VA_ARGS__专用标识符,代表与...对应的参数
   *
   */
#define TEST(condition,...) ((condition)? \
              (printf("test passed:%s\n",#condition)): \
              (printf(__VA_ARGS__)))

  TEST(3>2,"3>2 %s","test");
  TEST(2>3,"output %d is not big than> %d\n",2,3);

  /*
   * 8. __func__标识符,与预处理器无关,每函数都可访问
   */
  printf("%s return",__func__ );
#undef N
  return 0;
}

3.4 程序设计

多文件程序的文件

标题
源文件.c结尾的全部文件
头文件惯例.h结尾的文件

#include 包含规则

#include 记号 ,如#include CPUFILE,CPUFILE是一个根据不同系统下不同架构的宏定义

头文件包含规则
#include <文件名> 搜寻系统头文件
#include "文件名" 搜寻当前目录头文件,后系统文件目录

3.5 IO

image.png

3.5.1 流

标题
文件指针File *fp1 C中对流的访问是通过文件指针实现
标准流
文件指针默认含义
stdin标准输入键盘
stdout标准输出屏幕
stderr标准错误屏幕
重定向
重定向输入:
强制程序从文件输入而不是从键盘输入
方法:前面加上字符<,如demo <in.dat
重定向输出:
强制程序从文件输出而不是从屏幕输出
方法:前面加上字符>,如demo >out.dat

3.5.2 文件

<stdio.h>支持的两种类型文件
文本文件字节表示字符,可检查或编辑文件
二进制文件字节不一定表示字符,字节组可以表示其他类型数据
打开、关闭、及其他操作
打开文件fopen(文件名,模式)参考模式表
关闭文件fclose(文件指针) 参考模式
打开的流附加文件freopen(文件名,模式,附加文件指针)附加文件指针一般为标准流或其他
临时文件1.File *tempfile(void)
2.char *tempnam(char *s)
1. tempfile,易用,缺点不能命名,按需保存起来
2. tempnam生成一个唯一的、可用于命名临时文件的字符串,你需要手动将其作为参数传递给其他函数(如 fopen())以创建实际的临时文件。请注意,在某些系统上,由于安全性问题而不推荐使用此函数。
文件缓冲1. fflush(文件指针)
2. void setbuf(文件指针,缓冲数组,<缓冲模式>,>使用缓冲大小>)
1. fflush清洗文件缓冲区
2. setbuff 按大小位置缓冲类型(_IOFBF,_IOLBF,_IONBF)控制缓冲流
文件重命名rename(旧名,新名)
文件删除remove(文件名)
文件定位1.fseek移动到文件的某些位置
2.ftell返回当前文件位置
3. rewind把文件位置设置在起始处
4.fgetpos获取文件位置
5.fsetpos设置文件位置
模式二进制文件文本文件
rbr
写(文件无需存在)wbw
追加(文件无需存在)aba
读和写(从文件头开始)r+b 或 rb+r+
读和写(如果文件存在就截去)w+b 或 wb+w+
读和写(如果文件存在就追加)a+b 或 ab+a+

代码练习片段链接

3.5.3 输入输出

输出类型函数表述
输出
printf()
向标准输出stdout写入内容
输出
fprintf(File*,const char *,...)
向任意输出流写入内容
字符串输出
sprintf(char*,const char *,...)
输出写入字符数组
字符串输出
snprintf(char*,size_t,const char *,...)
输出写入字符数组,限制长度
字符输出
putchar(int)
向标准流stdout写一个字符
字符输出
fputc(int,File*)
任意流写一个字符
字符输出
putc(int,File*)
任意流写一个字符,效果同上
输出
puts(const char *)
向标准流stdout写字符串
输出
fputs(const char *,File *)
向任意流写字符串
输出
fwrite(void*,size_t,size_t,File*)
将内存中的数组复制给流,控制大小
输入类型函数表述
输入
scanf()
从标准输入stdin读入内容
输入
fscanf(File*,const char *,...)
从任意输入流读入内容
字符串
sscanf(char*,const char *,...)
输入写入字符数组,通常用fgets后,再使用sscanf进一步处理
字符
getchar(void)
从标准流stdin读一个字符,#define getchar) getc(stdin)
字符
fgetc(File*)
从任意流读一个字符
字符
getc(File*)
从任意流读一个字符,效果同上
字符
ungetc(int,File*)
从任意流读入的字符“放回”并清除流的文件末尾指示器

gets(char *)
从标准流stdin 读一行

fgets(char *,int n,File *)
从任意流读字符串,到n-1个,或换行符结束操作

fread(void*,size_t,size_t,File*)
从流读入到数组的元素,控制大小

四.标准库

image.png

主要分为上面几个部分,参考上篇

总结

  • 整体上包括了C语言语法的概要,本身目的是为了少量描述进行知识概括,但对宏和指针部分还是保留一些细节描述
  • 对于Android JNI,Framework知识所需的进程线程部分知识还需单独文章罗列。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
自动控制节水灌溉技术的高低代表着农业现代化的发展状况,灌溉系统自动化水平较低是制约我国高效农业发展的主要原因。本文就此问题研究了单片机控制的滴灌节水灌溉系统,该系统可对不同土壤的湿度进行监控,并按照作物对土壤湿度的要求进行适时、适量灌水,其核心是单片机和PC机构成的控制部分,主要对土壤湿度与灌水量之间的关系、灌溉控制技术及设备系统的硬件、软件编程各个部分进行了深入的研究。 单片机控制部分采用上下位机的形式。下位机硬件部分选用AT89C51单片机为核心,主要由土壤湿度传感器,信号处理电路,显示电路,输出控制电路,故障报警电路等组成,软件选用汇编语言编程。上位机选用586型以上PC机,通过MAX232芯片实现同下位机的电平转换功能,上下位机之间通过串行通信方式进行数据的双向传输,软件选用VB高级编程语言以建立友好的人机界面。系统主要具有以下功能:可在PC机提供的人机对话界面上设置作物要求的土壤湿度相关参数;单片机可将土壤湿度传感器检测到的土壤湿度模拟量转换成数字量,显示于LED显示器上,同时单片机可采用串行通信方式将此湿度值传输到PC机上;PC机通过其内设程序计算出所需的灌水量和灌水时间,且显示于界面上,并将有关的灌水信息反馈给单片机,若需灌水,则单片机系统启动鸣音报警,发出灌水信号,并经放大驱动设备,开启电磁阀进行倒计时定时灌水,若不需灌水,即PC机上显示的灌水量和灌水时间均为0,系统不进行灌水。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值