Finalización de conocimientos básicos de lengua C

Prefacio

Este artículo organiza los conocimientos básicos del lenguaje C como referencia diaria en el desarrollo.

Tipo de datos C

Tipos de descripción
tipo básico Son tipos aritméticos, incluidos dos tipos: tipos enteros y tipos de punto flotante
Tipo enumerado También son tipos aritméticos y se utilizan para definir variables a las que solo se les puede dar un cierto valor entero discreto en el programa.
Tipo derivado Incluyen: tipos de puntero, tipos de matriz, tipos de estructura, tipos de unión y tipos de función.
tipo vacío El especificador de tipo void indica que no hay ningún valor disponible.

printf

Formato de código descripción
%C personaje
%re Entero con signo
%yo Entero con signo
%mi Notación científica, use e minúscula
%MI Notación científica, use mayúscula E
%F Punto flotante
%gramo Utilice el más corto de% e o% f
%GRAMO Utilice el más corto de% E o% f
% O Octal
% s Cadena de caracteres
% u Entero sin signo
%X Número hexadecimal sin signo, use letras minúsculas
%pags Un puntero
%norte El parámetro debe ser un puntero a un número entero que apunte a la posición donde se coloca el número de caracteres
%% Una señal

Puntero de caracteres y matriz de caracteres

    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);

Puntero de matriz y matriz de puntero

  • Puntero de matriz (puntero int):
    se puede decir que el puntero de matriz es "puntero de matriz", que apunta a la primera dirección de una matriz.
    int (*p)[2]: Puntero de matriz, pz apunta a una matriz que contiene 2 valores de tipo 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);
}

  • Matriz de punteros (matriz int) Se
    puede decir que una matriz de punteros es una "matriz de punteros", es decir, cada elemento de esta matriz es una variable de puntero, en un sistema de 32 bits, un puntero ocupa cuatro bytes.
    int *p[2]: Matriz de puntero, una matriz de variable de puntero int de longitud 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]);

Punteros de función y funciones de puntero

  • Puntero de función (puntero int), el puntero apunta a una función
int (*pfun)(int, int)// pfun是一个指向返回值为int的函数的指针
  • Función de puntero (función int), es decir, la función que devuelve el puntero:
    nombre de tipo * nombre de función (columna de lista de parámetros de función);
int *pfun(int, int); // pfun是一个返回值为整型指针的函数

Puntero puntero constante y constante

    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;

Punteros y referencias

&El operador de dirección, a través del cual se puede obtener el
valor de dirección de una variable; el operador de valor *también se llama desreferencia, a través del cual se pueden obtener los datos correspondientes a la ubicación de una dirección; la "solución" es corresponder a la dirección La cosa se descomprime y descomprime, al igual que al abrir un paquete, ese es el valor de la variable, por lo que se llama "desreferencia". En otras palabras, desreferenciar es devolver el objeto correspondiente en la dirección de memoria.

Los punteros y las referencias son un concepto, normalmente decimos que una referencia a una determinada variable es un puntero a la variable.
Pero los punteros y las variables de puntero son dos conceptos. Es decir, una variable almacena la dirección de otra variable, luego la variable se llama variable de puntero.
La longitud del puntero es de 4 bytes por debajo de 32 bits y de 8 bytes por debajo de 64 bits.

La esencia de los punteros es el acceso indirecto.

  • Escenarios de uso del puntero: transferencia y compensación
    Principio de llamada de función: transferencia de valor; el valor del parámetro real se asigna al parámetro formal (principio de compilación)

Desplazamiento del puntero

    // 需要区分清楚指针++的操作
    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]);

Almacenamiento dinámico y almacenamiento estático

Desde la perspectiva del alcance de las variables, se puede dividir en variables globales y variables locales; desde la perspectiva de la existencia de valores variables, se puede dividir en métodos de almacenamiento estático y métodos de almacenamiento dinámico;
métodos de almacenamiento estático: se refiere a la asignación fija por parte del sistema durante la operación del programa El método del espacio de almacenamiento; Método de
almacenamiento dinámico: Es el método de asignar dinámicamente el espacio de almacenamiento de acuerdo con las necesidades durante la ejecución del programa;

  • Existen las siguientes categorías de almacenamiento: De acuerdo con la categoría de almacenamiento de la variable, se puede conocer el alcance y la vida útil de la variable
    auto: Las variables locales que no se declaran como estáticas son todos métodos de almacenamiento dinámicos y el símbolo de reparación predeterminado es automático. Estos espacios de almacenamiento se liberan automáticamente al final de la función.
    static: Asigne espacio en el área de almacenamiento estático, asigne valores durante la compilación e inicialice solo una vez.
    extern: Tomar prestada una variable definida en otro archivo para indicar que se trata de una declaración pero no de una definición. Deje que el compilador consulte su definición en otro lugar ;:
    registerRegistre variables, rara vez utilizadas;

  • consejos: La
    función es externa de forma predeterminada;
    función estática modificada significa válida en este archivo;
    variable estática modificada significa variable estática y válida en este archivo;
    variable local estática: aunque es estática, otras funciones no pueden hacer referencia a ella.

Operador

image.png

Estructura

La estructura está alineada; la
estructura se pasa por valor en la función y todos los contenidos se copian directamente en el espacio de la pila, por lo que el valor de la persona que llama no cambiará; es necesario usar el puntero para pasar la referencia, y el pase de referencia puede evitar este problema.

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

typedef 和 #define

  • typedefPermite a los programadores crear alias para tipos existentes, que pueden hacer código que sea comentario;
    generalmente, el nombre definido se representa con letras mayúsculas en la definición de typedef para recordar a los usuarios que el nombre del tipo es en realidad una abreviatura de símbolo.
    Cuando use una typedef para nombrar un tipo de estructura, puede omitir la etiqueta de la estructura:
    Cuando use una typedef, recuerde que typedef no crea ningún tipo nuevo, solo agrega una etiqueta conveniente a un tipo existente.

  • #defineTambién conocido como definición de macro, el identificador es el nombre de la macro definida, denominada macro.
    Su característica es: el identificador definido no ocupa memoria, sino un símbolo temporal, que no existe después de la precompilación. La precompilación también se denomina preprocesamiento. La precompilación no es compilación, sino procesamiento antes de la compilación. Esta operación la completa automáticamente el sistema antes de la compilación formal.

    • De hecho, la operación realizada por la compilación previa es un simple reemplazo de "texto".
      Para las definiciones de macros, cuando se compilan previamente, todos los lugares donde aparece el "identificador" en el programa se reemplazarán con esta "constante", que se denomina "reemplazo de macro" o "expansión de macro".
      Una vez que se completa el reemplazo, se realiza la compilación formal. Entonces, cuando hace clic en "Compilar", se realizan dos operaciones, es decir, precompilar primero y luego compilar formalmente. #include <stdio.h> es el mismo, es decir, en el preprocesamiento, primero simplemente reemplace la línea #include <stdio.h> en el programa con todo el contenido de "texto" en el archivo de encabezado stdio.h, y luego proceda a la compilación formal.
  • typedefY #definela diferencia entre
    diferentes de #define, typedef, el nombre del símbolo creado está limitado solo por el tipo de valor que no se puede usar.
    Typedef es interpretado por el compilador, no por el preprocesador.
    Dentro de su alcance limitado, typedef es más 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是指针,文本替换而已;

archivo de cabeza

#Indica que este es un comando de preprocesamiento. Todo lo que comience con "#" es un comando de preprocesamiento.

Un archivo de encabezado es un archivo, generalmente en forma de código fuente, que el compilador incluye automáticamente al procesar otro archivo fuente.
El archivo de encabezado generalmente coloca las declaraciones de variables, macros, variables globales del sistema y prototipos de funciones definidos en el archivo .c con el mismo nombre, y las declaraciones que deben usarse fuera del .c.

  • Función de archivo de encabezado:
    1. Desarrollo conveniente: contiene constantes comunes, estructuras, definiciones de tipos, funciones y declaraciones de variables requeridas por algunos archivos;
    2. Haga que el alcance de la función comience desde la posición de la declaración de función, no desde la posición de la definición de función (resumen de la práctica)
    3. Proporcionar interfaz: un paquete de software puede proporcionar una interfaz al mundo exterior (por ejemplo: stdio.h).

Dos sintaxis de #include

  • #include <file>
    Este formulario se utiliza para hacer referencia a los archivos de encabezado del sistema. Busca un archivo llamado archivo en la lista estándar de directorios del sistema. Al compilar el código fuente, puede usar la opción -I para poner el directorio antes de la lista.

  • #include "file"
    Este formulario se utiliza para hacer referencia a los archivos de encabezado del usuario. Busca un archivo llamado archivo en el directorio que contiene el archivo actual. Al compilar el código fuente, puede usar la opción -I para poner el directorio antes de la lista.

Solo cite el archivo de encabezado una vez

  • El # ifndef / # define / # endif se agrega al archivo de encabezado para evitar que el archivo de encabezado se cite repetidamente.

  • Una referencia duplicada
    significa que un archivo de encabezado se incluye varias veces en el mismo archivo cpp. Este error a menudo se debe a la inclusión anidada.
    Por ejemplo: hay un archivo ah #include "ch" y el archivo b.cpp importa #include "ah" e #include "ch" en este momento, hará que ch se cite repetidamente.

  • Consecuencias causadas por referencias repetidas a archivos de encabezado:

    1. Las referencias duplicadas en algunos archivos de encabezado solo aumentan la carga de trabajo del trabajo de compilación y no causarán demasiados problemas. Es solo que la eficiencia de compilación es menor, pero para proyectos grandes, será doloroso tener una eficiencia de compilación baja.
    2. Algunos archivos de encabezado se incluyen repetidamente, lo que puede causar errores, como definir variables globales en el archivo de encabezado (aunque este método no se recomienda, pero sí lo permite la especificación C), lo que puede causar definiciones repetidas.
  • #ifndef HEADER_FILESignifica "si no define HEADER_FILE" si HEADER_FILE no existe

#ifndef HEADER_FILE
#define HEADER_FILE

the entire header file file

#endif

Esta estructura se conoce comúnmente como envoltorio #ifndef. Cuando se hace referencia al archivo de encabezado nuevamente, la condición es falsa porque HEADER_FILE ya está definido. En este punto, el preprocesador omitirá todo el contenido del archivo y el compilador lo ignorará.

#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 entera

/*
 * 符号常量,简单替换,使用时要注意。
 */
#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);
}

Complemento

El código de complemento es para expresar números negativos
Código de complemento = código original invertido + 1

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

Variable entera

El bit más alto del número con signo representa el signo;
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); // 溢出,使用时防止溢出
}

Tipo de punto flotante

Tipo de caracter

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

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

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

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

Supongo que te gusta

Origin blog.csdn.net/u014099894/article/details/111842231
Recomendado
Clasificación