C

C语言基础知识

kokomi
2025-02-05 / 0 评论 / 6 阅读 / 正在检测是否收录...

define

extern "C"(已完成):在C++中,如果需要使用c语言中的库文件,可以使用extern "C"去包含

一、基础知识

1、头文件

头文件(通常以.h结尾)主要用于声明函数、宏、结构体、联合体、枚举类型等,它们的主要作用是提供接口,使得多个源文件(通常以.c结尾)可以共享这些声明。头文件不应该包含函数或全局变量的定义,这样会导致多重定义问题,即多个源文件可能包含相同的定义,导致链接时错误

1、extern 和extern "c"

extern常常放在函数的前面成为函数声明的一部分,那么,C语言的关键字extern在函数的声明中起到什么作用?

  • 函数默认是extern的,下述两个函数声明没有明显的区别;
  • 显式使用extern关键字可以增强代码的可读性,表明这个函数是在另一个文件中定义的;

    extern int f();
    int f();

当然,这样的用处还是有的,就是在程序中取代include "*.h"来声明函数;在一些复杂的项目中,我比较习惯在所有的函数声明前添加extern修饰,关于这样做的原因和利弊可见下面这个例子:

(1)在test1.h中有下列声明:

#ifndef TEST1H
#define TEST1H

extern char g_str[];     // 声明全局变量g_str
void fun1();
#endif

(2)在test1.cpp中:

#include "test1.h"

char g_str[] = "123456";    // 定义全局变量g_str
void fun1(){
    cout << g_str << endl;
}

(3) 以上是test1模块,它的编译和链接都可以通过,如果我们还有test2模块也想使用g_str,只需要在原文件中引用就可以了;test2.cpp如下:

#include "test1.h"

void fun2(){
    cout << g_str << endl;
}

以上test1和test2可以同时编译链接通过,如果你感兴趣的话可以用ultraEdit打开test1.obj,可以在里面找到"123456"这个字符串,但是却不能在test2.obj里面找到,这是因为g_str是整个工程的全局变量,在内存中只存一份,test2.obj这个编译单元不需要再有一份了,不然在链接时报告重复定义的错误

(4)有些人喜欢把全局变量的声明和定义放在一起,这样可以防止忘记定义,如把上面的test1.h改为:

extern char g_str[] = "123456";    // 这个时候相当于没有extern

然后把test1.cpp中g_str的定义去掉,这个时候再编译test1和test2这两个模块时,会报链接错误,这是因为把全局变量的定义放在了头文件之后,test1.cpp这个模块包含了test1.h,而test2.cpp也包含了test1.h,所以再一次定义了g_str,这个时候链接器在链接test1和test2时发现两个g_str

如果你非要把g_str的定义放在test1.h中,那么就需要把test2.cpp中的#include "test1.h"去掉换成:

extern char g_str[];

void fun2(){
    cout << g_str << endl;
} 

这个时候编译器就知道g_str是一个引自外部的一个编译模块了,不会在模块中重复定义一个;但是这样做非常糟糕,因为你无法在test2.cpp中使用#include "test1.h"了,那么test1.h中声明的其他函数你也无法使用了,除非也都用extern修饰,这样的话光声明的函数就要一大串,而且头文件的作用就是要给外部提供接口使用的。所以请记住,只在头文件中做声明,不应包含函数和全局变量的定义

extern 和const、extern和static:https://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777431.html

4、头文件和库文件的搜索路径

头文件

头文件通常以.h结尾

一般有两种形式的写法:双引号和尖括号(当然也可以直接写绝对路径,那就不会有查找路径的问题了),如下:

#include <iostream>
#include "demo.h"

它们查找路径的顺序是有区别的,双引号形式会查找当前路径,而尖括号形式不会,具体查找顺序为:

1、当前目录(仅双引号形式)

2、编译时指定的路径(CMake中target_include_directories配置的搜索路径、CMake中include_directories配置的搜索路径、gcc -I指定的搜索路径)

3、系统环境变量CPLUS_INCLUDE_PATH(C++头文件)或C_INCLUDE_PATH(C头文件)指定的目录

4、gcc默认目录:/usr/include; /usr/local/include; /usr/lib/gcc/x86_64-linux-gnu/5/5include (注:最后一个路径是gcc程序的库文件路径,各个用户的系统上可能不一样)

如果各个目录下存在相同的文件,则先找到哪个就使用哪个,这时顺序很重要

gcc的默认目录与安装gcc时指定的--prefix有关,该值可通过gcc -v查看,具体的目录可通过echo | g++ -v -x c__ -E -查看,如下:

~: echo | g++ -v -x c++ -E -
...
#include "..." search starts here:
#include <...> search starts here:
/usr/local/include
/opt/buildtools/gcc-7.3.0/include
/opt/buildtools/gcc-7.3.0/gcc/x86_64-pc-linux-gnu/7.3.0/include-fixed
/usr/include
...

添加路径到头文件的搜索路径:一般使用CMakeLists.txt或Makefile

库文件

库文件可以分为静态库(.a文件)和动态库(.so文件)两种

链接时库文件的搜索路径为:

1、编译时指定的库文件目录(由gcc -L参数指定)
2、环境变量LIBRARY_PATH指定的目录
3、系统默认目录:/lib; /usr/lib; /usr/local/lib;

一般用户安装的库会安装在/usr/local/lib,系统自带的库位于/lib 和 /usr/lib,用户自己编译的库要使用-L参数指定

一、数组

在C语言中,定义数组只能使用常量

可变长数组?

二、字符串函数

fgets:从文件/终端读取字符串

函数原型:char fgets(char str, int n, FILE *fp);
函数参数:str为字符数组,n为要读取的字符数目,fp为文件指针
函数返回值:读取成功返回字符串数组首地址,即str;读取失败返回NULL

注意:

  • 读取到的字符串会在末尾自动添加'\0',n个字符也包括'\0';也就是说,实际只读到n-1个字符
  • 在读取到n-1个字符之前如果出现了换行,或者读取到了文件末尾,则读取结束;这意味着,不管n的值多大,fgets最多只能读取一行数据,不能跨行
  • fgets遇到换行时,会将换行符一并读取到字符数组中
  • fgets也可以从标准输入(即终端)读取一行字符,将第三个参数改为stdin即可

fgets使用如下:

// 逐行读取文件
#include <stdio.h>
#include <stdlib.h>

#define N 100

int main(){
    FILE *fp = fopen("demo.txt", "rt");
    char str[N+1];
    IF(fp == NULL){
        printf("Cannot open file\n");
        exit(0);
    }
    while(fgets(str,N,fp) != NULL){
        printf("%s", str);
    }
    fclose(fp);
    return 0;
}

假设demo.txt如下:

123
456
789

终端输出结果和demo.txt一致,每次读取到的字符串中的str[3]即为换行(ASCII码 10),str[4]为'\0'(ASCII码 0)

fputs:将字符串写入文件的函数

函数原型:int fputs(char str, FILE fp);

函数参数:str为要写入的字符串,fp为指向待写入文件的指针

返回值:若成功则返回写入的字符个数,失败返回EOF

注意:

  • fputs也可以将字符串写入标准输出(即终端),只需将第二个参数改为stdout
  • fputs不会自动在字符串末尾添加一个换行符,它只会写入直到遇到'\0'
  • 如果遇到换行符,会将换行符写入

strlen:获取字符串的长度

函数原型:int strlen(char const *string);

返回值:返回字符串中字符的个数,不会包括'\0'结束符

strcpy/strncpy:字符串拷贝函数

函数原型:char strcpy(char dest, char *src);

​ char strncp(char dest, char *src, int size);

dest指向目标字符串,src指向源字符串;函数的作用为将源字符串拷贝到目标字符串

参数:dest:指向用于存储复制内容的目标数组;src:要复制的字符串

返回值:返回指向目标字符串的指针

注意:

  • 将src
0

评论 (0)

取消