语法基础——C语法基础

前言

马上是就到大四的我,面临校招的我倍感压力,而且今年又是安卓的瓶颈期,外面对能力的要求特别的高,但是我看了很多面经之后,发现很多大公司注重的是数据结构与算法,所以我们不得不有两手准备,开始温习数据结构与算法

当然要学习算法和数据结构,那么C语言是必须先学习的,因为大部分算法和数据结构都是以C语言作为使用语言的,所以这篇文章是针对Android程序员的,由于有了Java的基础,所以有一些语法并没有介绍全面

常量与变量

1、常量:C语言常量的声明有两种方式

  1. 使用define关键字
  2. 使用const关键字

它们的区别是:

  1. define是在编译时,会自动将声明的变量替换到程序中
  2. const是在运行是,会自动将声明的变量替换到程序中,同时,const可以很容易知道常量的类型
#include<stdio.h>

#define YOU_AGE 23 
const int HIS_AGE = 25;

int main(){

    return 0;
}

2、变量

C语言基本数据类型有:short、int、long、char、float、double

int a = 10;
char b = 'A';
float c = 25.5;
double d = 25.5;

这里可以提前说(char *)这个类型,这个是字符指针,我们可以把它当作Java的String类型

3、自定义变量类型名称

使用typedef关键字可以自定义数据类型的名称

typedef int my_int;

int main(){
    //相当于int b = 10
    my_int b = 10;
    return 0;
}

流程控制与循环

1、if、if-else语句

int a = 10;
int b = 5;
if(a>b){

}else{

}

2、switch语句

switch(表达式){ 
    case 常量表达式1:  语句1;
    case 常量表达式2:  语句2;
    … 
    case 常量表达式n:  语句n;
    default:  语句n+1;
}

3、goto语句

goto语句是跳转语句,可以将执行顺序跳转到指定的标签中

//实现1+2+3...+100的和
int i = 1;
loop: if(i<=100){
    sum=sum+i;
    i++;
    goto loop;
}

4、for语句

int i,sum;
for( i=1; i<=100; i++ ){  
    sum=sum+i;
}

5、while、do-while语句

  1. while语句是先判断循环条件之后,再决定执不执行循环体
  2. do-while语句是先执行一次循环体,再判断循环条件,决定执不执行循环体
int i=1,sum;
while(i<=100){
    sum=sum+i;
    i++;
}

do{
    sum=sum+i;
    i++;
}while(i<100);

运算符

1、算术运算符

<、<=、>、>=、==、!=、赋值运算符(=)

2、逻辑运算符

&&(与)、||(或)、!(非)

3、位运算符

&(位与)、|(位或)、~(位反)、^(异或)、>>(右移)、<<(左移)

4、三目运算符

条件表达式 ? 结果1 : 结果2

5、sizeof运算符

用来计算变量、常量、数据类型所占用存储空间的字节数

6、自增自减运算符

++a,a++,–a,a–

输出和输入

1、输出

//输出字符
char ch = 'a';
putchar(ch);
//输出字符串
puts("hello word");
printf("hello c");

2、格式化输出

数据类型数据格式
int%d
short%d
long%ld
float%f
double%lf
char%c
十六进制%X
八进制%o
字符串%s


//输出整型
printf("number is %d",23);
//输出十六进制
printf("%X",0xFF00FF);
//输出八进制
printf("%o",8);
//输出浮点型
printf("%f",3.14);
//输出字符
printf("%c",'A');
//输出字符串
printf("%s","Hello C");

3、输入

//输入字符
char answer = getchar();
printf("%c",answer);

4、格式化输入

//输入整型
int a;
scanf("%d",&a);
//输入字符
char c;
scanf("%c",&c);
//输入字符串
char buf[100];
scanf("%s",buf);

这里需要注意的是,scanf()函数第二个参数需要一个数据的内存地址,用&符号可以指向一个内存地址,而char[]类型,本身就是个地址,所以不用加&符号

数组

1、一维数组

//创建指定大小一维数组
int arr[10];
//创建自动指定大小一维数组
int arr[] = {5,6,8,9};

2、二维数组

int arr[3][4] = {
    {1,2,3,4},
    {5,6,7,8},
    {9,10,11,12}
};

3、字符数组

//字符数组的第一种表现形式,其值是可以修改的
char str[10] = {'H','e','l','l','o','/0'};
str[0] = "h";
//字符数组的第二种表现形式,其值是不可以修改的
char *str = "Hello";
//输出字符串
printf("%s",str);

这里需要注意字符数组凡是遇到有’/0’出现就判定该字符数组已经到结尾,默认结尾是会自动补上的,否则输出时会出现中文乱码

函数

1、无返回值的函数

void printfStr(){
    printf("Hello World");
}

2、有返回值的函数

int add(int a,int b){
    return a+b;
}

3、可变参数函数

int sum(int n,...){
    int i;
    int all = 0;
    va_list args;
    va_start(args,n);
    for(i=0;i<n;i++){
        all+=va_arg(args,int);
    }
    va_end(args);
    return all;
}

完整代码如下

#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h>
//函数需要声明才能调用
int add(int a,int b);
void printfStr();
int sum(int n,...);

int main(){
    int c = add(2,3);
    int d = sum(1,2,3);
    printfStr();
    return 0;
}
//有返回值的函数
int add(int a,int b){
    return a+b;
}
//无返回值的函数
void printfStr(){
    printf("Hello World");
}
//可变参数函数
int sum(int n,...){
    int i;
    int all = 0;
    va_list args;
    va_start(args,n);
    for(i=0;i<n;i++){
        all+=va_arg(args,int);
    }
    va_end(args);
    return all;
}

指针

1、变量指针

指针可以理解为Java里面数组,它指向数据存储的内存地址,默认指针是指向数组的第一个数据

int a = 10;
//定义指针,指向a的存储地址
int *pa = &a;
//获取数据的两种方式,输出都是10
printf("%d",*pa);
printf("%d",pa[0]);
//获取数据的内存地址
printf("%#x",pa);

2、无类型指针变量

无类型指针可以指向任何类型的变量

void *str = "hello world";
void *a = 5;

printf("%s",str);
printf("%d",a);

3、函数指针

指针指向的是一个内存地址,不仅是变量内存地址,函数内存地址也可以

#include<stdio.h>
#include<stdlib.h>
void say(int a,int b){
    printf("Hello");
}
int main(){
    void(*func_p)(int,int) = &say;
    func_p(0,0);
}

4、自定义函数名

使用typedef关键字自定义函数名

#include<stdio.h>
#include<stdlib.h>
void say(int a,int b){
    printf("Hello");
}
typedef void(*Func)(int ,int);
int main(){
    Func method = &say;
    method(0,0);
}

5、指针总结

  • 指针有类型,地址没有类型
  • 地址只是开始的位置,类型表示在什么地方结束
  • 空指针默认值为0,但系统不允许访问空指针地址

结构体

1、结构体

结构体就相当于Java的Bean对象,可以把(struct+结构体名)当作实体对象,进行对象的声明

struct File{
    char *name;
    int size;
};

int main(){
    //第一种赋值方式
    struct File file;
    file.name = "abc.txt";
    file.size = 10;
    //第二种赋值方式
    struct File file = {"abc.txt",10};
}

当然,还可以使用typedef关键字将结构体起个别名,使其更像Java的实体对象

struct _File{
    char *name;
    int size;
};

typedef struct _File File;

int main(){
    File file;
    file.name = "abc.txt";
    file.size = 10;
}

也有另一种简化的写法,效果是一样的,为了使代码更好看

typedef struct _File{
    char *name;
    int size;
}File;

int main(){
    File file;
    file.name = "abc.txt";
    file.size = 10;
}

2、指针结构体

#include<stdio.h>
#include<stdlib.h>
//实体对象
typedef struct{
    char *name;
    int size;
}File,*File_p;
//创建文件
File * createFile(char *name,int size){
    //第一种写法
    File *f = malloc(sizeof(File));
    f->name = name;
    f->size = size;
    return f;
    //第二种写法
    File_p file_p = malloc(sizeof(File_p));
    file_p->name = name;
    file_p->size = size;
    return file_p;
}
//删除文件
void deleteFile(File *file){
    free(file);
}

int main(){
    File *f = createFile("abc.txt",10);
    printf("%s,%d",f->name,f->size);
    deleteFile(f);
}

3、嵌套结构体

//第一种写法
struct Teacher{
    char *name;
    int age;
};

struct Student{
    char *name;
    int age;
    //嵌套结构体声明
    struct Teacher t;
};

//第二种写法
struct Student{
    char *name;
    int age;
    //嵌套结构体声明
    struct Teacher{
        char *name;
        int age;
    }t;
};

void getTeacher(){
    struct Student s1;
    s1.age = 10;
    s1.name = "hensen";
    //嵌套结构体赋值
    s1.t.name = "hensen's teacher";
}

4、结构体数组

void structArr(){
    struct Teacher ts[] = { {"hensen",21}, {"hensen2",22} };
    //遍历结构体数组
    struct Teacher *t = ts;
    for (; t < ts +2; t++){
        printf("%s,%d\n", t->name, t->age);
    }

    system("pause");
}

5、结构体大小(字节对齐)

//大小为16字节,其大小是最宽的基本数据类型的整数倍
//提升读取效率
struct Man{
    int age;
    double weight;
};

共同体(联合体)

共同体从字面上可以理解使用共同内存地址的一种结构,即共同体里面的属性所存储的值都是一样的,因为它们的内存地址都指向一个地方(共同体内存地址最大长度 = 所有属性中内存地址长度最大的那个属性)

typedef union _Fmaily{
    char a;
    int b;
}Fmaily;

int main(){
    Fmaily f;
    f.b = 97;
    //输出97的ASCLL值,即a
    printf("%c",f.a);
    //输出97
    printf("%d",f.b);
    return 0;
}

枚举

枚举只是用来列举所有情况,枚举默认的值从1开始

enum Day{
    Monday,
    Tuesday,
    Webnesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
};

int main(){
    //枚举的值,必须是括号中的值
    //值为1
    enum Day day = Monday;
}

文件操作

1、写文件

//打开一个文件,参数一:文件名,参数二:读写模式(w表示写,r表示读)
FILE * f = fopen("abc.txt","w");
if(f != NULL){
    //写进字符
    fputc('A',f);
    //写进字符串
    fputs("Hello \nWord",f);
    //关闭文件流
    fclose(f);
}

2、读文件

通过一串一串的读取

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

void main(){
    char *path = "D:\\untitled\\my.py";
    //打开文件
    FILE * file = fopen(path, "r");
    if (file == NULL){
        printf("文件打开失败");
        return;
    }
    //读取文件
    char buff[1024];
    while (fgets(buff, 1024, file)){
        printf("%s",buff);
    }
    //关闭文件流
    fclose(file);
    system("pause");
}

通过一个一个字符读取

void readFile(){
    int i;
    //读文件
    FILE * f = fopen("abc.txt", "r");
    if (f != NULL){
        char buf[100];
        //清空buf里面的内容
        memset(buf, 0, 100);
        for (i = 0; i<100; i++){
            //读取字符
            char ch = fgetc(f);
            //如果ch不是End of FIle
            if (ch != EOF){
                buf[i] = ch;
            }
            else{
                break;
            }
        }
        printf("%s", buf);
        fclose(f);
    }
    return 0;
}

3、文件的加密和解密

加密

void crpypt(char normal_path[],char crypt_path[]){
    //打开文件
    FILE *normal_fp = fopen(normal_path, "r");
    FILE *crypt_fp = fopen(crypt_path, "w");
    int ch;
    while ((ch = fgetc(normal_fp)) != EOF){ //End of File
        //写入(异或运算)
        fputc(ch ^ 9,crypt_fp);
    }
    //关闭
    fclose(crypt_fp);
    fclose(normal_fp);
}

解密

void decrpypt(char crypt_path[], char decrypt_path[]){
    //打开文件
    FILE *normal_fp = fopen(crypt_path, "r");
    FILE *crypt_fp = fopen(decrypt_path, "w");
    int ch;
    while ((ch = fgetc(normal_fp)) != EOF){ //End of File
        //写入(异或运算)
        fputc(ch ^ 9, crypt_fp);
    }
    //关闭
    fclose(crypt_fp);
    fclose(normal_fp);
}

使用

void main(){
    char *normal_path = "E:\\dongnao\\vip\\ndk\\08_08_C_05\\files\\friends.txt";
    char *crypt_path = "E:\\dongnao\\vip\\ndk\\08_08_C_05\\files\\friends_crypt.txt";
    char *decrypt_path = "E:\\dongnao\\vip\\ndk\\08_08_C_05\\files\\friends_decrypt.txt";

    //加密
    crpypt(normal_path, crypt_path);
    //解密
    decrpypt(crypt_path, decrypt_path);

    getchar();
}

4、二进制文件的复制

计算机文件存储在物理上都是二进制,读写文本文件与二进制文件的区别体现在回车换行符中

  • 写文本时,每遇到一个’\n’,会将其转换成’\r\n’
  • 读文件时,每遇到一个’\r\n’,会将其转换成’\n’
void copyFile(){
    char *path = "D:\\workspaceweb\\CSDNMovie\\assets\\imgs\\bg.jpg";
    char *new_path = "D:\\workspaceweb\\CSDNMovie\\assets\\imgs\\new_bg.jpg";
    FILE * file = fopen(path, "rb");
    FILE * new_file = fopen(new_path, "wb");
    int buff[1024];
    int len = 0;
    while ((len = fread(buff, sizeof(int), 1024, file)) != 0){
        fwrite(buff, sizeof(int), len, new_file);
    }
    fclose(file);
    fclose(new_file);
}

5、获取文件的大小

void readFileSize(){
    char *path = "D:\\workspaceweb\\CSDNMovie\\assets\\imgs\\bg.jpg";
    FILE *file = fopen(path, "r");
    //fseek:移动文件流的读写位置
    //0是偏移量,SEEK_END表示文件末尾
    fseek(file, 0, SEEK_END);
    //ftell:取得文件流的读取位置
    long fileSize = ftell(file);
    printf("%d", fileSize);
}

内存分配

1、C语言内存分配:

  1. 栈区(自动分配、释放):windows下,栈内存分配2M(确定的常数),超出了限制,提示stack overflow错误
  2. 堆区(手动分配、释放):操作系统80%内存
  3. 全局区或静态区
  4. 字符常量区
  5. 程序代码区

2、分配内存的两种方法

int len = 10;
int *p = calloc(len,sizeof(int));
int *p = malloc(sizeof(int)* len);

3、动态分配内存的注意点

  1. 不能多次释放
  2. 释放完之后(指针仍然有值),给指针置NULL,标志释放完成
  3. 内存泄露(p重新赋值之后,再free,并没有真正释放内存)

4、内存分配例子

void getMalloc(){

    int len = 10;
    int *p = malloc(sizeof(int)* len);
    printf("%#x \n", p);

    int i = 0;
    for (; i < len; i++){
        p[i] = rand() % 100;
    }

    int addLen = 5;
    //realloc方法
    //参数一:原来的指针
    //参数二:重新分配的总长度
    //重新分配内存的两种情况:
    //缩小,缩小的那一部分数据会丢失
    //扩大,有以下三种情况
    //1. 如果当前内存段后面有需要的内存空间,直接扩展这段内存空间,realloc返回原指针
    //2. 如果当前内存段后面的空闲字节不够,那么使用堆中的第一个能够满足这一要求的内存块,将目前的
    //数据复制到新的位置,并将原来的数据释放掉,返回新的内存地址
    //3. 如果申请失败,返回NULL,原来的指针仍然有效
    int *p2 = realloc(p, sizeof(int)*(len + addLen));
    printf("%#x", p2);
    if (p2 == NULL){
        printf("重新分配内存失败");
    }

    i = 0;
    for (; i < (len + addLen); i++){
        p[i] = rand() % 100;
    }

    if (p != NULL){
        free(p);
        p == NULL;
    }
    if (p2 != NULL){
        free(p2);
        p2 == NULL;
    }
    getchar();
}

预编译处理

1、define指令作用

  • 定义标识
  • 定义常数
  • 定义宏函数

2、定义标识

//1、标识支持C++语法
#ifdef __cplusplus
//2.1、防止文件重复引入,老版写法
#ifndef AH
#define AH
#include "B.H"

void printA();

#endif
//2.2、防止文件重复引入,新版写法
#pragma once
#include "B.h"

void printA();

3、定义常数

#define MAX 100

4、定义宏函数

void com_jni_read(){
    printf("read\n");
}

void com_jni_write(){
    printf("write\n");
}
//NAME是参数
#define jni(NAME) com_jni_##NAME();
//使用的时候
void main(){
    jni(write);
}

5、定义多参数的宏函数

#define LOG(LEVEL,FORMAT,...) printf(##LEVEL); printf(##FORMAT,__VA_ARGS__);
#define LOG_I(FORMAT,...) LOG("INFO:",##FORMAT,__VA_ARGS__);
#define LOG_E(FORMAT,...) LOG("ERROR:",##FORMAT,__VA_ARGS__);
#define LOG_W(FORMAT,...) LOG("WARN:",##FORMAT,__VA_ARGS__);
//使用的时候
void main(){
    LOG_E("%s%d","大小:",89);
}

常用例子

1、获取0-100随机数

void getRandom(){
    //生成随机种子
    int len = 10;
    int i = 0;
    int *p = malloc(sizeof(int)*len);
    srand((unsigned)time(NULL));
    for (; i < len; i++){
        //生成0-100的随机数
        p[i] = rand() % 100;
        printf("%d\n", p[i]);
    }

    if (p!= NULL){
        free(p);
        p = NULL;
    }

    getchar();
}

2、操作cmd

void openMspaint(){
    system("mspaint");
    system("pause");
}

3、注入Dll

__declspec(dllexport) void injectDll(){
    //属性(项目右键)-> 常规(配置类型)-> .dll类型
    //生成(选择栏)-> 生成解决方案
    //使用DLLInject工具
    int *p = 0x2ff9d8;
    *p = 99999;
}

C语言函数速查文档下载

  • 9
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许英俊潇洒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值