Finition des connaissances de base en langage C

Préface

Cet article organise les connaissances de base du langage C pour référence quotidienne dans le développement.

Type de données C

Types de la description
type de base Ce sont des types arithmétiques, dont deux types: les types entiers et les types à virgule flottante
Type énuméré Ce sont également des types arithmétiques et sont utilisés pour définir des variables qui ne peuvent recevoir qu'une certaine valeur entière discrète dans le programme
Type dérivé Ils incluent: les types de pointeur, les types de tableau, les types de structure, les types d'union et les types de fonction.
type de vide Le spécificateur de type void indique qu'aucune valeur n'est disponible.

printf

Format de code la description
% c personnage
%ré Entier signé
%je Entier signé
% e Notation scientifique, utilisez e minuscule
% E Notation scientifique, utilisez E majuscule
%F Point flottant
%g Utilisez le plus court de% e ou% f
%G Utilisez le plus court de% E ou% f
% O Octal
% s Chaîne de caractères
% u Entier non signé
%X Nombre hexadécimal non signé, utilisez des lettres minuscules
% p Un pointeur
% n Le paramètre doit être un pointeur vers un entier pointant vers la position où le nombre de caractères est placé
%% Un panneau

Pointeur de caractères et tableau de caractères

    char *p = "hello"; // 字符串常量池地址赋值给p,即p指向了字符串常量池的hello
    char *p1 = "hello";
    char c[10] = "hello"; // 是把字符串常量池里的hello strcpy给了c

    printf("%p, %p, %p\n", p, p1, c); // p和p1指向同一个地方
    printf("%d, %d, %d, %d\n", (p == p1), (*p == *p1), (c == p), (*c == *p));
    printf("%c, %c\n", *c, *p);

    c[0] = 'H'; // 可以赋值,在栈空间上
//    p[0] = 'H'; // 会崩溃,在常量池里,不可写
    puts(c);

    char *p2 = malloc(10 * sizeof(char));
    strcpy(p2, "hello");
    p2[0] = 'H';
    puts(p2); // 可以修改,在堆上分配

    printf("===");

    char *p3 = malloc(10 * sizeof(char));
    p3 = "hello"; // 把常量池hello地址赋给p3,造成内存泄露,刚才在堆上分配的10个空间大小没法释放
//    p3[0] = 'H'; // 不能修改,因为是常量池的
    puts(p3); // 可以修改,在堆上分配

    p = "world";
    strcpy(c, "world"); // 重新给c赋值
    puts(p);
    puts(c);

Pointeur de tableau et tableau de pointeurs

  • Pointeur de tableau (pointeur int): le
    pointeur de tableau peut être considéré comme un "pointeur de tableau", pointant vers la première adresse d'un tableau.
    int (*p)[2]: Pointeur de tableau, pz pointe vers un tableau contenant 2 valeurs de type int
// 传递数组指针,列数为2;
void printArr(int (*p)[2], int row) {
    
    

    // *p指向行,*(*p)指向具体元素
    for (int i = 0; i < row; ++i) {
    
    
        for (int j = 0; j < sizeof(*p) / sizeof(int); ++j) {
    
    
            printf("%d,", *(*(p + i) + j));  // *p指向行,*(*p)指向具体元素
            printf("%d,", p[i][j]);  // 同样可以这么访问
        }
        printf("\n");
    }
}

void test() {
    
    

    int zippo[4][2] = {
    
    {
    
    2, 4},
                       {
    
    6, 8},
                       {
    
    1, 3},
                       {
    
    5, 7}};
    int (*pz)[2]; // 数组指针,pz指向一个内含2个int类型值的数组
    pz = zippo; // 该二维数组首地址就是一个数组指针

    printArr(zippo, 4); 
    printArr(pz, 4);
}

  • Tableau de pointeurs (tableau int) Un
    tableau de pointeurs peut être considéré comme un "tableau de pointeurs", c'est-à-dire que chaque élément de ce tableau est une variable de pointeur; dans un système 32 bits, un pointeur occupe quatre octets.
    int *p[2]: Tableau de pointeurs, un tableau de variables de pointeur int de longueur 2.
    char *a = "1";
    char *b = "2";
    char *arrA[] = {
    
    a, b}; // 字符串数组,用指针数组来表达
    char *arrB[] = {
    
    "1", "2"}; // 字符串数组,用指针数组来表达
    printf("%s,%s\n", arrA[0], arrA[1]);
    printf("%s,%s\n", arrB[0], arrB[1]);

Pointeurs de fonction et fonctions de pointeur

  • Pointeur de fonction (pointeur int), le pointeur pointe sur une fonction
int (*pfun)(int, int)// pfun是一个指向返回值为int的函数的指针
  • Fonction pointeur (fonction int), c'est-à-dire la fonction qui renvoie le pointeur:
    nom du type * nom de la fonction (colonne de la liste des paramètres de la fonction);
int *pfun(int, int); // pfun是一个返回值为整型指针的函数

Pointeur constant et pointeur constant

    int a = 1;
    int b = 2;

    // 常量指针,指针指向的值不可用改,但指针的指向可以改
    const int *p = &a;
    // *p = b; 错误,指向的值不可用改
    p = &a; // 指针的指向可以改

    // 指针常量,指针的指向可以改,指针指向的值可以改
    int *const p2 = &a;
    *p2 = b; // 指针指向的值可以改
    // p2 = &b; 错误,指针的指向不可用改

    // const修饰指针和常量,都不可用改
    const int *const p3 = a;
    // *p3 = b;
    // p3 = &b;

Pointeurs et références

&L'opérateur d'adresse, par lequel la valeur d'adresse d'une variable peut être obtenue; l'
opérateur de valeur est *également appelé déréférencement, à travers lequel les données correspondant à l'emplacement d'une adresse peuvent être obtenues; la «solution» est de correspondre à l'adresse La chose est décompressé et décompressé, tout comme l'ouverture d'un package, c'est-à-dire la valeur de la variable, on l'appelle donc "déréférencer". En d'autres termes, le déréférencement consiste à renvoyer l'objet correspondant dans l'adresse mémoire.

Les pointeurs et les références sont un concept, nous disons généralement qu'une référence à une certaine variable est un pointeur vers la variable.
Mais les pointeurs et les variables de pointeur sont deux concepts. Autrement dit, une variable stocke l'adresse d'une autre variable, puis la variable est appelée une variable pointeur.
La longueur du pointeur est de 4 octets sous 32 bits et de 8 octets sous 64 bits.

L'essence des pointeurs est l'accès indirect.

  • Scénarios d'utilisation du pointeur: transfert et décalage
    Principe de l'appel de fonction: transfert de valeur; la valeur du paramètre réel est affectée au paramètre formel (principe de compilation)

Décalage du pointeur

    // 需要区分清楚指针++的操作
    int arr[3] = {
    
    1, 5, 9};
    int *p;
    int j;

    p = arr;
    j = *p++;
//    j = (*p)++;
//    j = *++p;
//    j = ++*p;
//    j = *p++;
    printf("arr[0]=%d, j=%d, *p=%d\n", arr[0], j, *p);

    j = p[0]++;
    printf("arr[0]=%d, j=%d, *p=%d, p[0]=%d", arr[0], j, *p, p[0]);

Stockage dynamique et stockage statique

Du point de vue de la portée des variables, elle peut être divisée en variables globales et variables locales; du point de vue de l'existence de valeurs de variable, elle peut être divisée en méthodes de stockage statique et méthodes de stockage dynamique;
méthodes de stockage statique: fait référence au allocation fixe par le système pendant le fonctionnement du programme La méthode de l'espace de
stockage Méthode de stockage dynamique: C'est la méthode d'allocation dynamique de l'espace de stockage en fonction des besoins lors de l'exécution du programme;

  • Il existe les catégories de stockage suivantes. Selon la catégorie de stockage de la variable, la portée et la durée de vie de la variable peuvent être connues
    auto: Les variables locales qui ne sont pas déclarées comme statiques sont toutes des méthodes de stockage dynamiques et le symbole de réparation par défaut est auto. Ces espaces de stockage sont automatiquement libérés à la fin de la fonction.
    static: Allouer de l'espace dans la zone de stockage statique, attribuer des valeurs lors de la compilation et initialiser une seule fois.
    extern: Emprunter une variable définie dans un autre fichier pour indiquer qu'il s'agit d'une déclaration mais pas d'une définition. Laisser le compilateur interroger sa définition ailleurs;:
    registerenregistrer des variables, rarement utilisées;

  • conseils: La
    fonction est externe par défaut;
    la fonction modifiée statique signifie valide dans ce fichier;
    variable statique modifiée signifie variable statique et valide dans ce fichier;
    variable locale statique: bien qu'elle soit statique, les autres fonctions ne peuvent pas la référencer.

Opérateur

image.png

Structure

La structure est alignée; la
structure est passée par valeur dans la fonction, et tout le contenu est directement copié dans l'espace de pile, donc la valeur de l'appelant ne sera pas modifiée; il est nécessaire d'utiliser le pointeur pour passer la référence, et la passe de référence peut éviter ce problème.

typedef struct student {
    
    
  int num;
  struct student *pNext; // 此时不能省略student结构体标签
} stu;

typedef 和 #define

  • typedefPermet aux programmeurs de créer des alias pour les types existants, ce qui peut créer du code qui est un commentaire;
    généralement, le nom défini est représenté par des lettres majuscules dans la définition typedef pour rappeler aux utilisateurs que le nom du type est en fait une abréviation de symbole.
    Lorsque vous utilisez un typedef pour nommer un type de structure, vous pouvez omettre l'étiquette de la structure:
    Lorsque vous utilisez un typedef, rappelez-vous que le typedef ne crée pas de nouveaux types, il ajoute simplement une étiquette pratique à un type existant.

  • #defineÉgalement connu sous le nom de définition de macro, l'identificateur est le nom de la macro définie, appelée macro.
    Sa caractéristique est: l'identifiant défini n'occupe pas de mémoire, mais un symbole temporaire, qui n'existe pas après la précompilation. La précompilation est également appelée prétraitement. La pré-compilation n'est pas une compilation, mais un traitement avant la compilation. Cette opération est automatiquement effectuée par le système avant la compilation formelle.

    • En fait, l'opération effectuée par pré-compilation est un simple remplacement de "texte".
      Pour les définitions de macros, lorsqu'elles sont précompilées, tous les endroits où «l'identifiant» apparaît dans le programme seront remplacés par cette «constante», appelée «remplacement de macro» ou «expansion de macro».
      Une fois le remplacement terminé, la compilation formelle est effectuée. Ainsi, lorsque vous cliquez sur "Compiler", deux opérations sont réellement effectuées, c'est-à-dire, précompiler d'abord, puis compiler formellement. #include <stdio.h> est le même, c'est-à-dire que dans le prétraitement, remplacez d'abord simplement la ligne #include <stdio.h> dans le programme par tout le contenu "texte" dans le fichier d'en-tête stdio.h, puis passez à la compilation formelle.
  • typedefEt #definela différence entre
    différent de #define, le nom de symbole typedef créé est limité uniquement par le type de valeur ne peut pas être utilisé.
    Typedef est interprété par le compilateur, pas par le préprocesseur.
    Dans sa portée limitée, typedef est plus flexible que #define.

#define PI 3.1415926 // 可以用于值

typedef int SOCKET; // 给int命个别名SOCKET,之后代码里可以使用SOCKET来表示socket的fd,做到了代码即注释,方便阅读
#define SOCKET int //等同于上句

typedef char* STRING;
STRING a,b; // 等同于 char *a, char *b

#define STRING char*
STRING a,b // 等同于char *a, b; 只有a是指针,文本替换而已;

tête de fichier

#Indique qu'il s'agit d'une commande de prétraitement. Tout ce qui commence par "#" est une commande de prétraitement.

Un fichier d'en-tête est un fichier, généralement sous forme de code source, qui est automatiquement inclus par le compilateur lors du traitement d'un autre fichier source.
Le fichier d'en-tête place généralement les déclarations de variables, de macros, de variables globales système et de prototypes de fonctions définis dans le fichier .c avec le même nom, ainsi que les déclarations qui doivent être utilisées en dehors du .c.

  • Fonction de fichier d'en-tête:
    1. Développement pratique: contient des constantes, des structures, des définitions de type, des fonctions et des déclarations de variables communes requises par certains fichiers;
    2. Faire en sorte que la portée de la fonction commence à partir de la position de la déclaration de fonction, et non de la position de la définition de fonction (résumé de la pratique)
    3. Fournir une interface: un progiciel peut fournir une interface avec le monde extérieur (par exemple: stdio.h).

Deux syntaxes de #include

  • #include <file>
    Ce formulaire est utilisé pour référencer les fichiers d'en-tête du système. Il recherche un fichier nommé file dans la liste standard des répertoires système. Lors de la compilation du code source, vous pouvez utiliser l'option -I pour placer le répertoire avant la liste.

  • #include "file"
    Ce formulaire est utilisé pour référencer les fichiers d'en-tête utilisateur. Il recherche un fichier nommé file dans le répertoire contenant le fichier courant. Lors de la compilation du code source, vous pouvez utiliser l'option -I pour placer le répertoire avant la liste.

Ne citez qu'une seule fois le fichier d'en-tête

  • Le # ifndef / # define / # endif est ajouté au fichier d'en-tête pour empêcher le fichier d'en-tête d'être cité à plusieurs reprises.

  • Une référence en double
    signifie qu'un fichier d'en-tête est inclus plusieurs fois dans le même fichier cpp. Cette erreur est souvent causée par une inclusion imbriquée.
    Par exemple: il y a un fichier ah #include "ch" et le fichier b.cpp importe #include "ah" et #include "ch" à ce moment, cela provoquera des citations répétées de ch.

  • Conséquences causées par des références répétées aux fichiers d'en-tête:

    1. Les références dupliquées dans certains fichiers d'en-tête ne font qu'augmenter la charge de travail du travail de compilation et ne causeront pas trop de problèmes. C'est juste que l'efficacité de la compilation est inférieure, mais pour les grands projets, ce sera une chose pénible d'avoir une faible efficacité de compilation.
    2. Certains fichiers d'en-tête sont inclus à plusieurs reprises, ce qui peut provoquer des erreurs, telles que la définition de variables globales dans le fichier d'en-tête (bien que cette méthode ne soit pas recommandée, mais elle est en effet autorisée par la spécification C), ce qui peut provoquer des définitions répétées.
  • #ifndef HEADER_FILESignifie "sinon définir HEADER_FILE" si HEADER_FILE n'existe pas

#ifndef HEADER_FILE
#define HEADER_FILE

the entire header file file

#endif

Cette structure est communément appelée wrapper #ifndef. Lorsque le fichier d'en-tête est de nouveau référencé, la condition est fausse car HEADER_FILE est déjà défini. À ce stade, le préprocesseur ignorera tout le contenu du fichier et le compilateur l'ignorera.

#define  定义一个预处理宏
#undef   取消宏的定义
#if      编译预处理中的条件命令, 相当于C语法中的if语句
#ifdef   判断某个宏是否被定义(#define过), 若已定义, 执行随后的语句
#ifndef  与#ifdef相反, 判断某个宏是否未被定义
#elif    若#if, #ifdef, #ifndef或前面的#elif条件不满足, 则执行#elif之后的语句, 相当于C语法中的else-ifelse    与#if, #ifdef, #ifndef对应, 若这些条件不满足, 则执行#else之后的语句, 相当于C语法中的else
#endif   #if, #ifdef, #ifndef这些条件命令的结束标志.
defined   与#if, #elif配合使用, 判断某个宏是否被定义
ifdef的使用和#if defined()的用法是一样的。
ifndef又和#if !defined()的用法一样。
#pragma  说明编译器信息
#warning 显示编译警告信息
#error   显示编译错误信息

Constante entière

/*
 * 符号常量,简单替换,使用时要注意。
 */
#define PI 3 + 2
// #define PI (3 + 2),这样就对了

void test(void) {
    
    
    int i = PI;
    int j = PI;
    printf("i = %d, j = %d\n", i, j);

    int k = PI * PI; // 3 + 2 * 3 + 2 = 11
    printf("k = %d", k);
}

Complément

Le code complémentaire consiste à exprimer des nombres négatifs
Code complémentaire = code original inversé + 1

0000 0000 0000 0000 0000 0000 0000 0101  5
1111 1111 1111 1111 1111 1111 1111 1011 -5  // 取反+1

Variable entière

Le bit le plus élevé du nombre signé représente le signe;
image.png

void test() {
    
    
    int i = 0x80fb0000; // 首位为符号位
    unsigned int ui = 0x80fb0000;
    short j = 32767; // 0111 1111 1111 1111
    long k = 0;

    printf("%d, %d, %d\n", sizeof(j), sizeof(i), sizeof(k));
    printf("%d, %u\n", i, ui); // 使用%u输出unsiged

    printf("%d\n", j);
    ++j;
    printf("%d", j); // 溢出,使用时防止溢出
}

Type à virgule flottante

Type de caractère

    char c = 'c'; // 1字节
    printf("%c\n", c);

    printf("abc\rd\n"); // \r回到行首,只输出d

    printf("abc\b\b"); // 退格

    char a = '\0'; // 空字符,用于标示字符串结尾,不是空格,打印不出
    printf("%c", a);

Je suppose que tu aimes

Origine blog.csdn.net/u014099894/article/details/111842231
conseillé
Classement