C语言基础

本文详细介绍了C语言的基础知识,包括基本数据类型、函数组成、十六进制表示、头文件的作用,以及字符常量和ASCII码。还深入讲解了运算符如自增、取地址,常用的输入输出函数如scanf和printf,C风格字符串的特性和处理,以及宏的使用,包括防止头文件重复包含。文章适合C语言初学者学习。
摘要由CSDN通过智能技术生成

一、基础常识

1.基本数据类型

bool:1字节
char:1字节
int: 4字节
float: 4字节
double:8字节


#include <iostream>
using namespace std;

int main(){
    bool flag;  //1
    char ch;    //1
    int integer;//4
    float fl;   //4
    double dl;  //8

    int len1 = sizeof(flag);
    int len2 = sizeof(ch);
    int len3 = sizeof(integer);
    int len4 = sizeof(fl);
    int len5 = sizeof(dl);

    cout<<len1<<endl<<len2<<endl<<len3<<endl<<len4<<endl<<len5<<endl;
}

输出:
在这里插入图片描述



2.一个函数的组成

一个完整的函数包括:

返回类型 函数名(参数列表)
:初始化列表
{
	函数体;
	return 返回值;
}

3.十六进制:0x

0x11是十六进制数

十六进制数十进制数二进制数
0x11170001 0001

4.C语言的头文件

头文件函数作用
#include <stdio.h>printf、scanf定义输入/输出函数
#include <string.h>strcpy字符串处理
#include <stdlib.h>malloc、free定义杂项函数及内存分配函数

5.字符常量、转义序列

1.字符常量
单引号括起来的是字符常量
双引号括起来的是字符串


2.转义序列
\后加1-3个八进制数 或 \x后加十六进制数,可转义为对应的ASCII码值

'\x32’是转16进制,代表ASCII码值为50

在这里插入图片描述



例题1:

在这里插入图片描述



6.ASCII码表

在这里插入图片描述

在这里插入图片描述


7.正斜杠与反斜杠

正斜杠 /
反斜杠 \


8.if就近原则

在这里插入图片描述


9.表达式

(1)赋值表达式
#include <stdio.h>

int main(){
    int a = 0, x = 1,y = 2;
    a = (x = y);
    printf("%d",a);  //输出结果为2
    return 0;
}

在这里插入图片描述

分析:while(0),不执行循环体


(2)逻辑表达式的短路运算

跳转链接:https://blog.csdn.net/Edward1027/article/details/131363511

在这里插入图片描述


10.return语句

在这里插入图片描述


11.for循环

for(单次表达式;条件表达式;末尾循环体){
	中间循环体;
}



二、运算符

1.自增运算符:i++ 与 ++i

结论:++i效率更高


2.取地址:&、取内容:*


3.三元表达式 / 三目运算符

1.条件表达式:要进行判断的表达式?真:假


2.举例:返回 a和b中较大的一个值:
(1)传统方法:if else实现

if(a>b) return a;
else    return b;

(2)条件运算符(三目运算符):

return (a>b)?a:b;



三、常用函数

1.scanf、printf 、fgets

(1)scanf:输入数据
scanf("%d",&a);

%d:Decimal(十进制)
%c:Char(字符)
%s:String(字符串)
%f:Float(浮点数)
%u:Unsigned int(无符号整数)
%p:Pointer(地址/指针)



例题1:
在这里插入图片描述

A.应该是%lf
B.应该是x+6
C.p+6正确,选C
D.p是指针,不是数组




(2)printf:输出数据
格式控制

%d:Decimal(十进制)
%c:Char(字符)
%s:String(字符串)
%f:Float(浮点数)
%u:Unsigned int(无符号整数)
%p:Pointer(地址/指针)


%-10.2f
% 表示开始格式说明符。
- 表示左对齐输出。如果没有这个符号,默认是右对齐。
10 表示输出的宽度。在这个例子中,输出的字符数至少是 10 个。如果数字本身不够长,将会根据对齐方式添加空格。
.2 表示浮点数格式,小数点后保留两位小数。



#include <stdio.h>

int main()
{
   // char a = "张三";//错误,char只能保存1B。保存字符串应该用char数组
    char a = 'a';
    printf("%c 的ASCII码为 %d\n",a,a);
    char b[4] = {"张"};  //字符数组,用来保存汉字,一个汉字2B
    printf("%s",b);
    printf("\n第一天\n");
    return 0;
}

拓展:对比一下C++的cout

C:输出 张三 👇

#include <stdio.h>

int main()
{
    char a[]= "??";
    printf("%s\n",a);
    char b[]="张三";
    printf("%s\n",b);
}

C++:输出 张三 👇

#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    string a = "张三";
    cout<<a<<endl;
}

显然C++的cout要好用很多,不需要像C要记住printf的参数。




(3)fgets:读取一行内容

C语言读取一行,fgets

fgets(buf,n,stdin

拓展:
C++读取一行,cin.getline

cin.getline(a,5);

(4)getchar()

在这里插入图片描述


2.sizeof( )

sizeof 运算符用于计算一个对象或类型所占的内存字节数

(1)字符串

C语言字符串是以\0结尾

int a = sizeof("Hello"); //6
int b = strlen("Hello"); //5

(2)指针

64位系统中,指针的大小是8字节。

void getArray(char a[10]){
    printf("%d", sizeof(a)); //8
}

这段代码中,sizeof(a)的值将是指针的大小,而不是数组a的实际大小。这是因为在getArray函数参数中,char a[10]实际上被当作char* a处理。当数组作为参数传递给函数时,它会退化为指向数组第一个元素的指针。因此,sizeof(a)在getArray函数中给出的是指针的大小,而不是数组的大小。



四、C风格字符串

1.以\0结尾

'\0' 称为 字符串结束符,英文称为 NULL,中文称为 空字符
C语言以'\0' 作为字符串结尾,占一个位。

注意:在定义字符串数组时,应该为’\0’预留空间,否则在使用字符串时可能会发生数组越界的问题



例题1:指出程序中的错误, 并改正程序

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

void main(){
    int len = strlen("hello");
    char *str = malloc(len*sizeof(char));
    strcpy(str, "hello");
    printf("%s\n", str);
    return;
}

答案:
①main函数的返回值类型是int,最后是return 0
②C字符串以\0结尾,要预留一个字符的空间。
③malloc默认是void *,必须强转

修改:

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

int main(){
    int len = strlen("hello"); 
    char *str = (char *)malloc((len+1)*sizeof(char)); //len+1
    strcpy(str, "hello");
    printf("%s\n", str);
    return 0;
}


2.char c[ ] 与 char *c

char c[ ] 是 字符数组,此时c也可以当作char型指针
char *c 只能是一个char型指针


例题1:请说明此程序在做什么操作,并指出程序中的错误, 并改正程序

#include <stdio.h>
void main(){
    char *temp1, *temp2;
    char *input = "hello,world";
    for(int i = 0; i < strlen (input); i++ ) {
        if( input[i] == ',') {
            input[i] = '\0';
            temp1 = input;
            temp2 = input[i+1];
        }
    }
    printf("%s\n", temp1);
    printf("%s\n", temp2);
    return;
}

答:
该程序期望以逗号为分隔符,将hello和world分别输出。
有以下错误:
int main
②使用strlen,未加头文件
char *input = "hello,world";,这样字符串就存储到了只读段(代码段),不可被修改。而input是char型指针,指向该只读段。
④temp1、temp2都是指针类型,temp2 = input[i+1];应改为temp2 = &input[i+1];

修改:

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

int main(){
    char *temp1, *temp2;
    char input[] = "hello,world";
    for(int i = 0; i < strlen (input); i++ ) {
        if( input[i] == ',') {
            input[i] = '\0';
            temp1 = input;
            temp2 = &input[i+1];
        }
    }
    printf("%s\n", temp1);
    printf("%s\n", temp2);
    return 0;
}

例题2:指出程序中的多处错误, 并改正程序
要求在GetMem中分配内存,改动请保持GetMen接口不变

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void GetMem(char **p){
    *p=(char *)malloc(100);
}
int main(){
    char **str;
    GetMem(str);
    strcpy(*str, "hello world");
    printf("%s", *str);
    return 0;
}

修改:

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

void GetMem(char **p){
    *p = (char *)malloc(100);
}

int main(){
    char *str;
    GetMem(&str);
    strcpy(str, "hello world");
    printf("%s", str);
    return 0;
}


3.C风格字符串的初始化、strcpy

数组声明时可初始化,若在数组声明时没有初始化,则只能一个一个赋值,不能整体赋值。可使用strcpy();

在这里插入图片描述


4.strlen

strlen()时,只计算纯粹的C风格字符串的长度,不考虑\0的长度

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

int main(){
    char str[15] = "hello!";
    printf("%d\n",strlen(str));
    return 0;
}

在这里插入图片描述



五、宏

1.防止头文件重复包含

若头文件为computer.h,则可以加宏命令,防止头文件重复包含。
【详见条件编译C++ (week4):Linux基础

#ifdef __Computer_H__
#define __Computer_H__
/* 头文件内容 */
#endif //__Computer_H__
#pragma once
/* 头文件内容 */

原理:
#ifndef:检查宏是否未定义。
#define:定义宏。
#endif:结束条件编译。

(1)第一次包含头文件时:
预处理器遇到#ifndef __TASK_H__,检查宏__TASK_H__是否未定义。因为这是第一次包含,这个宏还没有定义。
由于__TASK_H__未定义,条件为真,继续执行后续代码。
预处理器遇到#define __TASK_H__,定义宏__TASK_H__
从而,整个头文件的内容会被包含到编译单元中。

(2)第二次及后续包含头文件时:
预处理器遇到#ifndef __TASK_H__,检查宏__TASK_H__是否未定义。由于在第一次包含时已经定义了这个宏,所以条件为假。
由于__TASK_H__已经定义,预处理器跳过#define __TASK_H__#endif之间的所有代码
因此,头文件的内容不会再次被包含,避免了重复定义。

(3)#endif 对应 #if#ifdef#ifndef 作为开头


头文件包含是一个递归(循环)的过程,如果被包含的头文件中还包含了其他的头文件,预处理器会继续将它们也包含进来;这个过程会一直持续下去,直到不再包含任何头文件,这与递归的过程颇为相似。
递归包含会导致一个问题,就是重复引入同一个源文件,如果不做任何处理,不仅会出现重复定义错误,而且不符合编程规范。
对此,可以通过宏保护来解决这个问题。



例题1:有一个头文件pfucert.h, 头文件被下面的宏包围,作用是什么。

#ifndef __PFUCERT_H__
#define __PFUCERT_H__
/* 头文件内容 */
#endif // _PFUCERT_H_

答:防止头文件被重复包含



2.三种预处理命令:include、宏定义、宏函数

#:预处理指令
#include :头文件复制过来
宏定义: 是 文本替换
宏函数:参数要括起来,整个表达式也要括起来。左括号紧贴宏函数名,否则会当成宏定义
在这里插入图片描述

为什么要用宏函数?
①宏函数快,仅仅是替换,避免了函数调用开销
应用场景:简短的、频繁调用的函数,写成宏函数,可以降低函数调用开销
②提供了一定的宏编程能力

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员爱德华

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

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

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

打赏作者

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

抵扣说明:

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

余额充值