首页 > 其他分享 >c语言基础

c语言基础

时间:2023-05-01 16:06:31浏览次数:30  
标签:语言 定义 int 基础 struct printf 变量 指针


环境设置

c程序的源文件通常使用扩展名.c

c程序需要编译成机器语言,这样cpu可以按给定指令执行程序。

最常用的编译器是gcc(mac上xcode就可以)

程序结构

  • #include 预处理器指令,类似于import,主要用于告诉编译器,我们要引入什么。
  • .h 结尾的是头文件,头文件中一般是定义的结构体和变量
  • #include <stdio.h> 引入头文件,告诉c编译器编译之前要引入stdio.h文件,在linux中去/usr/include目录中寻找
  • 函数
  • 变量
  • 语句&表达式
  • 注释 以//包裹的会被编译器忽略
#include <stdio.h>
 
 /*主函数,代表程序从这里开始*/
int main()
{
   /* printf是引入的stdio.h中的函数*/
   printf("Hello, World! \n");
   /*终止main函数,并返回0*/
   return 0;
}

基本语法

分号;

';'分号是语句结束符,每个语句必须以分号结束,#include 和#include 不需要加;

注释:

和java一样

// 单行注释  
/*多行
注释
*/

标识符

就是java里的变量名和java的变量规范差不多;

  • C 标识符是用来标识变量、函数,或任何其他用户自定义项目的名称,
  • 标识符以字母或下划线_开头,后可跟数字
  • C标识符内不允许出现标点符号,如@、$和%
  • C区分大小写,大小写不同是两个标识符

关键字

保留关键字,不能作为常量名、变量名等其他标识符

关键字	说明
auto	声明自动变量
break	跳出当前循环
case	开关语句分支
char	声明字符型变量或函数返回值类型
const	定义常量,如果一个变量被 const 修饰,那么它的值就不能再被改变
continue	结束当前循环,开始下一轮循环
default	开关语句中的"其它"分支
do	循环语句的循环体
double	声明双精度浮点型变量或函数返回值类型
else	条件语句否定分支(与 if 连用)
enum	声明枚举类型
extern	声明变量或函数是在其它文件或本文件的其他位置定义
float	声明浮点型变量或函数返回值类型
for	一种循环语句
goto	无条件跳转语句
if	条件语句
int	声明整型变量或函数
long	声明长整型变量或函数返回值类型
register	声明寄存器变量
return	子程序返回语句(可以带参数,也可不带参数)
short	声明短整型变量或函数
signed	声明有符号类型变量或函数
sizeof	计算数据类型或变量长度(即所占字节数)
static	声明静态变量
struct	声明结构体类型
switch	用于开关语句
typedef	用以给数据类型取别名
unsigned	声明无符号类型变量或函数
union	声明共用体类型
void	声明函数无返回值或无参数,声明无类型指针
volatile	说明变量在程序执行中可被隐含地改变
while	循环语句的循环条件

基本运算

和java差不多,运算符,简写等

a += b; 等价于  a = a+b;
 ++  -- 和java 也一样,在前先操作再使用,在后,先使用再操作

不同点:C的取余运算必须是整数,java的取余可以是小数

转义字符

  • \n 换行 (也表示字符串的结束)
  • \t 水平制表(相当于按tab)
  • \b 退格,将当前位置移到前一列
  • \f 换页,将当前位置移到下页开头
  • \r 回车
  • \v 垂直制表
  • ’ 单引号
  • " 双引号
  • \ 反斜杠

数据类型

  • char 字符型 以单引号’'包围
  • short 短整形
  • int 整形
  • long 长整形
  • float 单精度浮点型
  • double 双精度浮点型
  • void 无类型
  • 字符串类型 以 双引号"" 包围

基本类型以及占用长度

32位编译器中各基本类型所占字节数

c语言基础_java

在64位编译器中各基本类型所占字节数

c语言基础_c语言_02

在16位编译器中各基本类型所占字节数

c语言基础_c语言_03

typedef unsigned char   uint8_t;     //无符号8位数 1字节
typedef signed   char   int8_t;      //有符号8位数 1字节
typedef unsigned int    uint16_t;    //无符号16位数 2字节
typedef signed   int    int16_t;     //有符号16位数 2字节
typedef unsigned long   uint32_t;    //无符号32位数 4字节
typedef signed   long   int32_t;     //有符号32位数 4字节
typedef float           float32;     //单精度浮点数 4字节
typedef double          float64;     //双精度浮点数 8字节

获取某个数据类型长度可以使用sizeof操作符

#include <stdio.h>
int main()
{
   short x = 11;
   int y = 4567;
   int short_length = sizeof x;
   int int_length = sizeof(y);
   printf("short length = %d,int length = %d \n",short_length,int_length)
   return 0;
}

类型转化

自动类型转化

自动类型转化是编译器根据代码的上下文环境自动判断的结果,是静默的。

遵循以下规则:

c语言基础_#include_04

强制类型转化

(类型) 变量

double d = 123.8;
//转化的结果保存到临时变量x里
int x = (int) d

格式控制符

格式空字符,它指明了以何种形式输出数据,以%开头

  • %d 输出整数,是decimal的简写
  • %hd 输出short int 类型,是short decimal的简写
  • %ld 输出long int,是 long decimal的简写
  • %c 输出一个字符
  • %s 输出一个字符串
  • %f 以十进制形式输出 float 类型
  • %lf 以十进制形式输出 double 类型
  • %e 以指数形式输出 float 类型,输出结果中的 e 小写
  • %E 以指数形式输出 float 类型,输出结果中的 E 大写
  • %le 以指数形式输出 double 类型,输出结果中的 e 小写
  • %lE 以指数形式输出 double 类型,输出结果中的 E 大写
  • %g 默认最多保留六位有效数字,包括整数部分和小数部分;%f 和 %e 默认保留六位小数,只包括小数部分
  • %X 表示以十六进制输出
  • %#X表示以十六进制形式输出,并附带前缀0X
int x=123;
int a = 'x';
putchar(a) //字符的专用输出
printf("%d %c", x,a);

const

const类似于java中的final,定义了以后,它的值不能被改变,在整个作用域中都保持固定。

//语法
const type name = value
//定义常量最大年龄为150
const int maxAge = 150;
//重新赋值会报错
maxAge=88;

const和指针

//指针指向的数据是只读的
const int *p1;
int const *p2;


//只读指针
int * const p3;

循环结构和选择结构

c语言中的if else用法和java中的用法一样

c语言中的逻辑运算符合java中的一样

  • && 与运算,对应数学中的且
  • || 或运算,对应数学中的 或
  • !非运算,对应数学中的 非
switch(表达式){
    case 整型数值1: 语句 1;
    case 整型数值2: 语句 2;
    ......
    case 整型数值n: 语句 n;
    default: 语句 n+1;
}

如:
switch(a){
    case 1: printf("Monday\n");
    case 2: printf("Tuesday\n");
    case 3: printf("Wednesday\n");
    case 4: printf("Thursday\n");
    case 5: printf("Friday\n");
    case 6: printf("Saturday\n");
    case 7: printf("Sunday\n");
    default:printf("error\n");
}

c语言中的循环和java没区别 break和continue的用法也没啥区别

// while 循环
#include <stdio.h>
int main(){
    int i, sum=0;
    i = 1;  //语句①
    while(i<=100 /*语句②*/ ){
        sum+=i;
        i++;  //语句③
    }
    printf("%d\n",sum);
    return 0;
}


//for循环

#include <stdio.h>
int main(){
    int i, sum=0;
    for(i=1/*语句①*/; i<=100/*语句②*/; i++/*语句③*/){
        sum+=i;
    }
    printf("%d\n",sum);
    return 0;
}

数组

c中的数组和java区别不大,需要注意的是c中的类型,没有java那么强。

未初始化的值就默认为对应类型的默认值。

//数组初始化
int  arr[3] ={1,2,3}

//不指定长度的初始化,直接打满
int arr[] = {1,3,4}
  • 对于short、int、long,就是整数 0;
  • 对于char,就是字符 ‘\0’;
  • 对于float、double,就是小数 0.0。

字符串

c中没有字符串的概念的。用数组来承载字符串,c中使用数组或者指针来间接地存储字符串。

用来存放字符的数组称为字符数组。字符数组实际上是一些列字符的集合,也就是字符串。

c语言规定,可以将字符串直接赋值给字符数组

char str[10] = {"yxkong"};
char str[10] = "yxkong"; 
char str[] = "yxkong"; //这种形式更加简洁,实际开发中常用
int len = strlen(str)
  • 字符串只有在定义的时候,可以一次性赋值
  • 一旦定义完,只能一个字符一个字符的赋值修改;
  • 在c语言中,字符串总是以"\0"作为结尾(\0是ASCII码中的第0个字符,英文也称为Null)
  • c中处理字符串时会从前往后逐个扫描,发现\0就认为是字符串的结尾。
  • 字符串的长度使用 strlen(str)
  • 字符串可以用printf()格式输出,也可以直接用puts()输出

字符串的操作

  • 用于输入输出的字符串函数,例如printf、puts、scanf、gets等,使用时要包含头文件stdio.h
  • 而使用其它字符串函数要包含头文件string.h。

字符串连接函数 strcat()

//将y拼接到x
// x 必须足够长,要不然会越界(相当于往x的数组中添加数据)
// 拼接的过程中会删除x中的\0,最终返回x的地址
strcat(x, y);

字符串复制函数 strcpy()

// 将y复制到x
// c会把y中的字符串拷贝到x中
strcpy(x, y);

字符串比较函数 strcmp()

比较的是ASCII码值

// x和y相同返回0
// x > y 返回>0的值
// x < y 返回<0的值
strcmp(x, y);

函数

将常用的代码以固定的格式封装成一个独立的模块,只要知道这个模块的名字就可以重复利用,这个模块叫函数。

C语言自带的函数称为库函数。其他公司或个人开发的库称为第三方库

c语言的函数和正常情况下的java方法差不多(先定义再使用)

c中允许先声明,再使用,声明函数可以理解为java的中的接口

#include <stdio.h>

//声明一个函数add
int add(int x,int y);
int add(int,int); //和上面的效率一样

int main(){
    int rst = add(5,6);
    printf("%d",result);
    return 0;
}
//声明函数add的定义(实现)
int add(int x,int y){
    return x+y;
}

c语言可以直接在程序中定义代码块(这块和java区别很大)

#include <stdio.h>
int main(){
    int x = 20;
    {
        int x = 30;
        //输出30
        printf("x=%d",x)
    }
    //输出20
    printf("x=%d",x)
}

变量作用域

c中的局部变量的作用域和java的局部变量一样

全局变量一般定义在函数外,它的默认作用域是整个程序,也就是所有的源文件,包括源文件.c和头文件.h文件。

给变量加上 static ,它的作用域就变成了当前文件。

建议全局变量全部大写,以_分隔

#include <stdio.h>
//全局变量
int rst =0;
//当前文件
int static x=10;
//声明函数add的定义(实现)
int add(int x,int y){
    return x+y;
}

int main(){
    int y=6;
    rst = add(x,y);
    printf("%d",result);
    return 0;
}

预处理命令

在c语言编译和链接之前,还需要对源文件进行一些文本方面的操作,比如文本替换、文件包含、删除部分代码等,这个过程叫做预处理。

  • 预处理命令是以#开头的命令

include命令

“#include” 叫做文件包含命令,用来引入对应的头文件(.h文件),是预处理命令的一种

  • #include命令只能包含一个头文件,多个头文件引用需要多个#include命令
  • 多次引入同一个头文件,效果一样
  • 文件包含允许嵌套
  • 头文件中只能包含变量和函数的声明,不能包含定义(头文件的定义类似于java的接口和常量)

define命令

"#define"叫做宏定义命令,宏定义:用一个标识符来表示一个字符串,如果在后面的代码中出现了该标识符,那么就全部替换成指定的字符串

  • 宏定义是用宏名来表示一个字符串
  • 宏定义不是说明或语句,在行末不必加分号,如果加上分号,替换的时候分号也会被带上;
  • 宏定义必须卸载函数之外,作用域为到源程序结束,可通过#undef终止;
//不带参宏定义语法
#define 宏名  字符串

#include <stdio.h>
#define M(3*x+5)
int main(){
    int x = 5;
    int sum = 5*M; //等价于 sum = 5*(3*x+5)
    printf("sum = %d",sum)
    return 0;
}

//带参宏定义语法
#define 宏名(形参列表)  字符串

#include <stdio.h>
#define MIN(x,y)  ((x>y)?y:x)

int main(){
    int x = 5,y=8;
    int min = MIN(x,y)
    printf("min = %d",min)
    return 0;
}

带参宏定义与函数的区别

  • 宏展开后仅仅是字符串的替换,不会对表达式进行计算
  • 宏在编译之前就被替换掉了,不会参与编译;
  • 函数是一段重复使用的代码,会被编译,会分配内存

条件编译

#if 整型常量表达式1
    程序段1
#elif 整型常量表达式2
    程序段2
#elif 整型常量表达式3
    程序段3
#else
    程序段4
#endif



#ifdef  宏名
    程序段1(如果定义了指定的宏名,则执行这块)
#else
    程序段2 (没有定义指定的宏名,则执行这段)
#endif


#ifndef 宏名
    程序段1 
#else 
    程序段2 
#endif

指针

  • 指针用来指向内存中字节的内存地址
  • 通过&来获取变量的内存地址
  • 如果一个变量存储了一份数据的指针,我们叫指针变量
  • 通过 * 变量名来表示一个指针变量,定义指针变量,必须变量前加 *
  • 通过指针变量可以获取内存上的数据(比如数组)
  • 指针可以直接加地址,如果是数组,可能比较好定位
  • 如果是变量+n表示当前地址向下几个字节
datatype *name
数据类型  指针名称
  • 表示这是一个指针变量,datatype表示该指针所指向的数据的类型,定义指针变量时,必须带上*
// 定义int 变量a,并初始化为100
int a = 100;
// 定义int 指针p_a 并把a的指针地址赋值给p_a
int *p_a = &a;
#include <stdio.h>
int main(){
    int a = 22,b=55;
    //指针变量ptr指向a的地址
    int *ptr = &a;
    //通过指针变量获取数据
    printf("%d",*ptr);
    int c = 15;
    //通过指针变量修改内存上的地址
    *ptr = b;
    //通过指针变量获取内存上的数据(最后a,b,c 都为55)
    c = *ptr
    
    
     printf("%d",*ptr);
     
    //str 本身就表示数组的首地址,不需要加&
    char str[20] = "yxkong";
    printf("%#X, %#X\n", &a, str);
    
   
    return 0;
}

关于* 和&的问题

int  a = 10;
int *pa = &a;
  • *&a 可以理解为 *(&a) ,&a 取地址, *取对应地址的值,最终等于a
  • &*pa 可以理解为&(*pa) *pa获取了a的数据,&又取地址,又变成了指针变量 pa

结构体

结构体可以理解为java中的类。

  • 结构体的定义使用struct
  • 结构体指针的获取也是&
struct 结构名 对象名;         //“struct 结构名”就是结构体的数据类型名

//定义结构体
struct Point{
    int x;
    int y;
};

//使用新定义的结构体Point的时候,必须struct Point 然后对象名
struct Point oPoint1={100,100};
struct Point oPoint2;



struct Point{
    int x;
    int y;
} p;

//结构体变量赋值
p.x = 15;
p.y = 22;

//获取结构体的指针
struct Point *ptr = &p;

//通过结构体指针获取结构体成员
(*ptr).x
//通过箭头直接获取结构体指针的成员变量
*ptr->x


//只使用两次的结构体
struct {
    int x;
    int y;
} pot1,pot2={34,26};

pot1.x = 11;

结构体数组

//表示一个班级有5个学生
struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在小组 
    float score;  //成绩
}class[5];

//定义结构体数组,并初始化
struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在小组 
    float score;  //成绩
}class[5] = {
    {"Li ping", 5, 18, 'C', 145.0},
    {"Zhang ping", 4, 19, 'A', 130.5},
    {"He fang", 1, 18, 'A', 148.5},
    {"Cheng ling", 2, 17, 'F', 139.0},
    {"Wang ming", 3, 17, 'B', 144.5}
};

使用typedef 定义结构体

typedef是c语言中给数据类型起新别名的。是为了编码方便,类似于语法糖。使用typedef后,简化了结构体的使用;

//语法
typedef oldName newName

//相当于typedef 给struct redisObject起了一个别名robj
typedef struct redisObject{
    ....
} robj;


/**
 * 定义一个新的结构类型redisObject
 * 使用typedef 为结构体起了一个别名 叫robj
 * 之后就可以在程序中直接使用robj 了
 */
typedef struct redisObject {
    unsigned type:4;  //定义一个无符号变量 type,长度为4位
    unsigned encoding:4;
    unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
                            * LFU data (least significant 8 bits frequency
                            * and most significant 16 bits decreas time). */
    int refcount;
    void *ptr; //定义一个指针,指针是以*号开头
} robj;



robj *createObject(int type, void *ptr) {
    robj *o = zmalloc(sizeof(*o));
    o->type = type;
    o->encoding = OBJ_ENCODING_RAW;
    o->ptr = ptr;
    o->refcount = 1;

    /* Set the LRU to the current lruclock (minutes resolution), or
     * alternatively the LFU counter. */
    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
        o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
    } else {
        o->lru = LRU_CLOCK();
    }
    return o;
}

当结构体中需要引用自己时

typedef struct tagNode
{
    char *pItem;
    struct tagNode *pNext;  //引用自己的指针,这样编译器才能识别
} *pNode;

c语言枚举

//语法
enum typeName{ valueName1, valueName2, valueName3, ...... };

//示例,枚举值默认从0开始,往后逐个加1
enum week{ Mon, Tues, Wed, Thurs, Fri, Sat, Sun };

//给枚举赋值
enum week{ Mon = 1, Tues = 2, Wed = 3, Thurs = 4, Fri = 5, Sat = 6, Sun = 7 };



//定义枚举变量
enum week a, b, c;

enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } a, b, c;

//枚举赋值
enum week a = Mon,b

共用体union

  • 共用体有时也称为联合或联合体
  • 结构体的各个成员会占用不同的内存,相关之间没有影响
  • 共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员
  • 共用体占用的内存等于最长的成员占用的内存,共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值
  • 由于成员在内存中对齐到一头,修改一个成员,其他的成员值随之改变
//定义共用体
union data{
    int n;
    char ch;
    double f;
};
//创建变量
union data a, b, c;

//定义并共用体并创建变量
union data{
    int n;
    char ch;
    double f;
} a, b, c;

c语言基础_c语言_05

位域

有些数据在存储时并不需要占用一个完整的直接,只需要占用一个或几个二进制位即可。c语言提供了位域的数据结构。

  • 指定某些成员变量所占用的二进制位数(Bit)
  • 通过 变量名:num 来表示,num不能超过变量类型的长度
  • 不同的编译器位域的存储规则不一样,但都是在尽量压缩存储空间
  • 当相邻成员的类型相同时,如果他们的位宽之和小于类型的大小,那么后面的成员紧邻前一个成员存储,直到不能容纳为止;
  • 如果他们位宽之和大于类型的大小,那么后欧美的成员将从新的存储单元开始,其偏移量为类型大小的整数倍;
  • 当相邻成员的类型不同时,不同的编译器有不同的实现方案,GCC会压缩存储,VC/VS不会
struct bs{
    unsigned m; //m没有限制,占用4个字节内存
    unsigned n: 4; //:后面的数字用来限定成员变量占用的位数  占用4位
    unsigned char ch: 6; //占用6位
};

无名位域,无名位域成员没有名称,只给出数据类型和宽度,一般用来填充或者调整成员位置

struct bs{
    int m: 12;
    int  : 20;  //该位域成员不能使用
    int n: 4;
};

上面的例子中,如果没有位宽为 20 的无名成员,m、n 将会挨着存储,sizeof(struct bs) 的结果为 4;有了这 20 位作为填充,m、n 将分开存储,sizeof(struct bs) 的结果为 8


标签:语言,定义,int,基础,struct,printf,变量,指针
From: https://blog.51cto.com/yxkong/6238929

相关文章

  • lua基础语法篇一
    打印print("helloworld")注释单行注释多行注释--这是单行注释--[[这是多行注释]]赋值s="HelloWorld"--多重赋值a,b="Stringa","Stringb"--交换值,类似pythona,b="Stringa","Stringb"a,b=b,a数据类型Lua有8种基本类型,如下表......
  • Android重力感应基础
    android中的很多游戏的游戏都使用了重力感应的技术,但其apidemo却并没有重力感应的实例(不知道是不是我没找到,找到的朋友麻烦告诉我一下,谢谢),因为开发的需要,就研究了一下重力感应这方面,因为网上关于这方面的东西比较少,所以写出来跟大家交流一下,算是抛砖引玉吧。(p......
  • Arduino入门必备基础知识(基础认证考前总结)
    最近开始准备考取Arduino的官方认证,但之前没有系统地学习,因此打算好好整理一下,一方面当作考试复习,另一方面给想学习Arduino的朋友们一些帮助在学习之前,建议大家先在Arduino中文社区看一下使用教程,里面有很多答疑帖,还有很多干货,值得一看:https://www.arduino.cn/thread-1066......
  • java 基础(5)在idea中对java程序打包运行
    第一步 第二步 第三步  src目录下 第四步   第五步:  ......
  • 【web 开发基础】PHP自定义回调函数之call_user_func_array()
    前言从上一篇文章中我们了解到,回调函数是将一个函数作为参数传递到调用的函数中。如果在函数的格式说明中出现callback类型的参数,则该函数就是回调函数。虽然可以使用变量函数去声明自己的回调函数,不过我们通常大多还是会通过借助 call_user_func_array() 函数去实现。通过借助......
  • linux的基础结构和常用的命令基本 Linux 命令的列表:
    linux的基础结构和常用的命令基本Linux命令的列表: Linux提供了大量的命令,利用它可以有效地完成大量的工作,如磁盘操作、文件存取、目录操作、进程管理、文件权限设定等。所以,在Linux系统上工作离不开使用系统提供的命令。要想真正理解Linux系统,就必须从Linux命令学起,通过基础......
  • MySQL基础命令 | ChatGPT问答记录
    问:MySQL基础命令ChatGPT:MySQL是一种流行的开源关系型数据库管理系统(RDBMS),以下是一些常见的MySQL基础命令:连接到MySQL服务器:mysql-uusername-ppassword-hhostname创建数据库:CREATEDATABASEdatabase_name;删除数据库:DROPDATABASEdatabase_name;选......
  • 2023-04-30:用go语言重写ffmpeg的resampling_audio.c示例,它实现了音频重采样的功能。
    2023-04-30:用go语言重写ffmpeg的resampling_audio.c示例,它实现了音频重采样的功能。答案2023-04-30:resampling_audio.c是FFmpeg中的一个源文件,其主要功能是实现音频重采样。音频重采样是指将一段音频数据从一个采样率、声道数或样本格式转换为另一种采样率、声道数或样本格......
  • Django - json_script 模板语言,将queryset转换为前端json数据
     models.pyclassUser(models.Model):name=models.CharField(verbose_name="Name",max_length=64) serializer.pyclassUserSerializer(serializers.ModelSerializer):classMeta:model=Userfields=["name",......
  • C语言打印上下金字塔的按位取反运算符的精妙用法
    在打印上下金字塔的通常语句用法应该都是像下面这种#include<stdio.h>intmain(){  intn; do{   for(inti=1;i<n;i++){     for(inta=0;a<n-i;a++){       printf("");     }    for(intj=0;j<2*i-1;j++){     ......