Résumé : Langage C et Java

1. La différence entre le code machine et le bytecode

1. Introduction

  • Code machine : il s'agit d'un code d'instruction binaire compilé à partir d'un code d'assemblage sur une plate-forme matérielle informatique spécifique via un assembleur.
  • Bytecode : Il s'agit d'un code intermédiaire, qui est un code d'instruction binaire qui n'est pas lié à une plate-forme informatique spécifique, compilé à partir du code source Java via un compilateur Java. Le bytecode est généralement exécuté sur la machine virtuelle Java.

2. Compilation de C et Java

Le langage C est un langage statique et compilé. Le code machine est généré au moment de la compilation et exécuté directement au moment de l'exécution, de sorte que la vitesse d'exécution est très rapide.

Java est un langage interprété qui doit traduire le code source en bytecode avant d'être exécuté par la machine virtuelle.

  • Lorsqu'un programme Java est en cours d'exécution, la machine virtuelle Java doit convertir le bytecode en code machine pour exécuter le programme. Ce processus est divisé en deux étapes. Tout d'abord, le code à chaud est instantanément compilé en code machine local grâce à la technologie de compilation juste-à-temps (JIT), puis le code machine local exécute l'opération du programme. Par conséquent, bien que le bytecode ne soit pas une instruction machine directement exécutée, il est finalement converti en code machine pour exécuter le programme.

2. Pourquoi les performances du langage C sont-elles meilleures que celles de Java ?

1. Consommation de la compilation à l'exécution

Le langage C génère directement le code machine exécuté par la machine, tandis que Java doit être compilé dans un code intermédiaire tel que le bytecode, puis la JVM doit compiler le bytecode en code machine pour une exécution réelle lors de l'exécution.

2. Le langage C n'a pas besoin de mécanisme de récupération de place

Le ramasse-miettes Java lui-même consomme également beaucoup de ressources, ce qui entraîne une diminution des performances du programme.

Par exemple : lorsqu'un programme Java est en cours d'exécution, il faut plus de temps et d'espace pour vérifier et nettoyer les objets inutilisés en mémoire. Ce processus doit constamment analyser la mémoire et déterminer quels objets peuvent être libérés. En raison du grand nombre d'objets pouvant être traités, ce processus consomme beaucoup de temps et d'espace.

Dans le langage C qui libère manuellement la mémoire, les programmeurs peuvent contrôler directement l'allocation et la libération de la mémoire, évitant la surcharge supplémentaire de la récupération de place, de sorte que le programme s'exécute plus rapidement.

De plus, en Java, il y aura certainement une fragmentation de la mémoire, ce qui entraîne également une faible utilisation de la mémoire

3. Avantages de Java et C

Le langage C et Java ont des avantages et des inconvénients différents en termes de performances.

  • Le langage C est un langage statique et compilé. Le code machine est généré au moment de la compilation et exécuté directement au moment de l'exécution, de sorte que la vitesse d'exécution est très rapide. Le langage C permet également un accès direct aux ressources matérielles , ce qui le rend très populaire dans le domaine de la programmation système et du développement embarqué.
    • Le langage C n'a pas de mécanisme automatique de récupération de place, obligeant les programmeurs à gérer manuellement la mémoire . Les variables et les données en langage C sont stockées en mémoire et le programme doit explicitement allouer et libérer de la mémoire, sinon des problèmes tels que des fuites de mémoire se produiront.
      • L'avantage de ceci est que, puisqu'aucun ramasse-miettes n'est requis, les performances sont élevées
      • L'inconvénient est que les exigences des programmeurs sont relativement élevées et qu'ils doivent toujours faire attention aux codes liés à la récupération de mémoire.
  • En revanche, Java est un langage interprété qui nécessite que le code source soit traduit en bytecode avant d'être exécuté par une machine virtuelle.
    • Bien que le compilateur JIT de la machine virtuelle Java puisse optimiser le code selon différents environnements et données, sa vitesse d'exécution est plus lente que celle du langage C. Java dispose également d'un mécanisme de récupération de place, qui affectera la vitesse d'exécution du programme dans une certaine mesure, en particulier lorsque la consommation de mémoire du programme est importante.
    • Cependant, Java a aussi ses avantages. Le modèle de programmation orienté objet de Java, la gestion automatique de la mémoire, la vérification de type renforcée et d'autres fonctionnalités lui confèrent une sécurité, une maintenabilité et une portabilité supérieures, et peuvent effectuer un développement logiciel à grande échelle de manière plus efficace.

donc:

  • Le langage C présente des avantages évidents en termes de performances et convient aux scénarios nécessitant une vitesse élevée et nécessitant de faire fonctionner directement le matériel de la machine.
  • Java, en revanche, convient au développement de logiciels à grande échelle qui nécessite une sécurité, une maintenabilité et une portabilité supérieures.

4. Accès direct en langage C au boîtier de ressources matérielles

Le langage C peut accéder directement aux ressources matérielles. La raison en est que le langage C est un langage compilé statique qui peut générer directement du code machine pour l'exécution et permet la manipulation directe des adresses mémoire pour contrôler le matériel. Ci-dessous un exemple :

#include <stdio.h>
#include <conio.h>
#include <bios.h>

int main(void) {
    int ch;
    int status = _bios_keybrd(_KEYBRD_READ);
    
    printf("status = %04X\n", status);
    
    while ((ch = getch()) != ESC) {
        printf("key code: %02X\n", ch);
    }
    
    return 0;
}

Ce programme utilise les caractéristiques du langage C, utilise la fonction _bios_keybrd dans le fichier d'en-tête bios.h pour lire l'état du clavier, puis effectue les opérations correspondantes en fonction des résultats de lecture . Cet accès direct aux ressources matérielles est très utile dans la programmation système et le développement embarqué.

En revanche, Java ne prend pas en charge l'accès direct aux ressources matérielles. En effet, l'un des principaux objectifs de conception du langage Java est d'assurer la sécurité du programme et d'éviter les problèmes de sécurité causés par l'accès direct aux ressources matérielles. Afin d'obtenir une compatibilité multiplateforme, la machine virtuelle Java (JVM) implémente une couche d'abstraction pour encapsuler et gérer uniformément les ressources système afin d'assurer la portabilité et la sécurité du programme. Par conséquent, les programmes Java ne peuvent pas exploiter directement les ressources matérielles et doivent exploiter indirectement les ressources matérielles via des API spécifiques, telles que Java Native Interface (JNI).

Bien que Java ne puisse pas exploiter directement des ressources matérielles telles que le langage C, Java peut toujours appeler des fonctions de langage C via JNI pour répondre aux exigences de fonctionnement du matériel. Cependant, il convient de noter que cela peut réduire la portabilité et la sécurité des programmes Java et nécessite une gestion manuelle de la mémoire et d'autres problèmes. Par conséquent, il est nécessaire de peser le pour et le contre et de choisir une méthode appropriée pour exploiter les ressources matérielles.

Ce qui suit continue de fournir quelques exemples de matériel d'accès C :

/* 案例:访问摄像头设备 */
int main()
{
    int fd;
    fd = open("/dev/video0", O_RDWR);   //以读写方式打开摄像头设备
    ioctl(fd, VIDIOC_QUERYCAP, &cap);   //查询设备属性
    ...
    close(fd);   //关闭摄像头设备
    return 0;
}

这段代码通过调用 Linux 系统提供的摄像头操作接口(open、ioctl、close)实现对摄像头设备的访问,并查询设备属性。
/* 案例:访问麦克风 */
int main()
{
    int fd;
    fd = open("/dev/dsp", O_RDONLY);   //以只读方式打开麦克风设备
    ioctl(fd, SOUND_MIXER_READ_VOLUME, &vol);  //读取音量
    ...
    close(fd);   //关闭麦克风设备
    return 0;
}

这段代码通过调用 Linux 系统提供的麦克风操作接口(open、ioctl、close)实现对麦克风设备的访问,并读取音量。
/* 案例:访问打印机 */
int main()
{
    FILE *fp;
    fp = fopen("/dev/usb/lp0", "w");   //以写方式打开打印机设备
    fprintf(fp, "Hello, printer!\n");   //向打印机写入数据
    fclose(fp);   //关闭打印机设备
    return 0;
}

这段代码通过 Linux 文件系统提供的文件操作接口(fopen、fprintf、fclose)实现对打印机设备的访问,并向打印机写入数据。

/* 案例:访问键盘 */
int main()
{
    int fd;
    struct input_event event;
    fd = open("/dev/input/event0", O_RDONLY);   //以只读方式打开键盘设备
    while(1)
    {
        read(fd, &event, sizeof(event));   //读取键盘事件
        if (event.type == EV_KEY && event.value == 1)   //判断是否为按键事件
        {
            printf("Key pressed: %d\n", event.code);   //输出按键编号
        }
    }
    close(fd);   //关闭键盘设备
    return 0;
}

这段代码通过调用 Linux 系统提供的输入设备操作接口(open、read、close)实现对键盘设备的访问,并读取键盘事件,并判断是否为按键事件,最后输出按键编号。
/* 案例:访问鼠标 */
int main()
{
    int fd;
    struct input_event event;
    fd = open("/dev/input/mice", O_RDONLY);   //以只读方式打开鼠标设备
    while(1)
    {
        read(fd, &event, sizeof(event));   //读取鼠标事件
        if (event.type == EV_REL && event.code == REL_X)   //判断是否为横向移动事件
        {
            printf("X moved: %d\n", event.value);   //输出横向移动距离
        }
    }
    close(fd);   //关闭鼠标设备
    return 0;
}

过调用 Linux 系统提供的输入设备操作接口(open、read、close)实现对鼠标设备的访问,并读取鼠标事件,并判断是否为横向移动事件,最后输出横向移动距离。
/* 案例:访问定时器 */
int main()
{
    int fd;
    struct itimerval timer;
    fd = open("/dev/rtc0", O_RDONLY);   //以只读方式打开定时器设备
    timer.it_value.tv_sec = 1;   //设置定时时间为1秒
    timer.it_value.tv_usec = 0;
    timer.it_interval.tv_sec = 1;   //设置定时周期为1秒
    timer.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &timer, NULL);   //设置定时器
    while(1)
    {
        ...   //执行定时任务
    }
    close(fd);   //关闭定时器设备
    return 0;
}
这段代码通过调用 Linux 系统提供的定时器操作接口(open、setitimer、close)实现对定时器设备的访问,并设置定时时间和周期,最后执行定时任务。

5. Fichiers .c et fichiers .h en langage C

Le fichier .c fait référence au fichier de code source du langage C, qui contient le code d'implémentation du langage C et constitue la partie centrale du programme. Il peut être compilé dans un fichier exécutable par un compilateur et exécuté directement sur l'ordinateur.

Le fichier .h fait référence au fichier d'en-tête (Header File) , qui est également un fichier de code source en langage C. Il contient des déclarations de fonctions, de variables et de constantes pour partager des implémentations entre plusieurs fichiers source. Le fichier d'en-tête est généralement inclus devant le fichier .c, et les fonctions et variables qui y sont définies peuvent être utilisées dans le fichier .c.

Par conséquent, les fichiers .c et .h constituent ensemble la structure de base d'un programme en langage C.

6. Macro en langage C

La macro en langage C fait référence à une instruction de prétraitement, également appelée définition de macro (Macro Definition). Il peut remplacer un ensemble de textes de code par un autre ensemble de textes de code pour simplifier le code et améliorer la réutilisabilité du code. Les définitions de macros sont généralement effectuées à l'aide de la directive #define.

Les macros sont très similaires aux constantes en Java, elles représentent essentiellement des valeurs qui ne peuvent pas être modifiées lors de l'exécution du programme .

Cependant, les macros peuvent être utilisées pour représenter des valeurs sous diverses formes telles que des constantes, des expressions, des fonctions, etc. Les constantes en Java ne peuvent représenter que des valeurs de types de base et de types de chaînes.

Les macros ne sont pas liées au type, il peut donc y avoir des erreurs de type. Les constantes en Java ont des restrictions de type, évitant le problème des erreurs de type.

Utilisez des définitions de macro pour définir des constantes : #define PI 3.1415926
De cette façon, PI peut être utilisé directement dans le code au lieu de 3.1415926, ce qui améliore la lisibilité et la maintenabilité du code.

Bien que la définition de macro puisse simplifier le code, il convient de noter que la définition de macro n'est qu'un simple remplacement de texte, il est donc facile de faire des erreurs qui ne sont pas faciles à trouver, comme oublier d'ajouter des parenthèses aux paramètres. De plus, les définitions de macro peuvent réduire la lisibilité du code et peuvent également augmenter la taille du code. Par conséquent, une attention particulière est requise lors de l'utilisation des définitions de macro.

Sept, préprocesseur C

Le préprocesseur C ne fait pas partie du compilateur, mais il s'agit d'une étape distincte du processus de compilation. En bref, un préprocesseur C n'est rien de plus qu'un outil de remplacement de texte qui demande au compilateur d'effectuer le prétraitement requis avant de compiler réellement. Nous abrégerons C Preprocessor en CPP .

Toutes les commandes du préprocesseur commencent par un signe dièse (#) . Il doit s'agir du premier caractère non nul et, pour des raisons de lisibilité, les directives du préprocesseur doivent commencer par la première colonne. Toutes les directives importantes du préprocesseur sont répertoriées ci-dessous :

Huit, fichier d'en-tête C

Un fichier d'en-tête est un fichier avec une extension  .h  , qui contient des déclarations de fonctions C et des définitions de macros, et est partagé par plusieurs fichiers source. Il existe deux types de fichiers d'en-tête : les fichiers d'en-tête écrits par les programmeurs et les fichiers d'en-tête fournis avec les compilateurs.

Pour utiliser un fichier d'en-tête dans un programme,   il doit être référencé à l'aide de la directive de prétraitement C #include .

 

Neuf, gestion de la mémoire C 

Le langage C fournit plusieurs fonctions d'allocation et de gestion de la mémoire. Ces fonctions se trouvent dans  le fichier d'en-tête <stdlib.h>  .

En langage C, la mémoire est gérée par des variables pointeur . Un pointeur est une variable qui stocke une adresse mémoire pouvant pointer vers une variable de n'importe quel type de données, y compris des entiers, des nombres à virgule flottante, des caractères et des tableaux. Le langage C fournit certaines fonctions et opérateurs qui permettent aux programmeurs d'opérer sur la mémoire, y compris l'allocation, la libération, le mouvement et la copie.

cas:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main()
{
   char name[100];
   char *description;
 
   strcpy(name, "Zara Ali");
 
   /* 动态分配内存 */
   description = (char *)malloc( 30 * sizeof(char) );
   if( description == NULL )
   {
      fprintf(stderr, "Error - unable to allocate required memory\n");
   }
   else
   {
      strcpy( description, "Zara ali a DPS student.");
   }
   /* 假设您想要存储更大的描述信息 */
   description = (char *) realloc( description, 100 * sizeof(char) );
   if( description == NULL )
   {
      fprintf(stderr, "Error - unable to allocate required memory\n");
   }
   else
   {
      strcat( description, "She is in class 10th");
   }
   
   printf("Name = %s\n", name );
   printf("Description: %s\n", description );
 
   /* 使用 free() 函数释放内存 */
   free(description);
}

 Lorsque le code ci-dessus est compilé et exécuté, il produit le résultat suivant :

Name = Zara Ali
Description: Zara ali a DPS student.She is in class 10th

Fonctions et opérateurs de gestion de mémoire couramment utilisés en langage C

  • Fonction malloc() : utilisée pour allouer dynamiquement de la mémoire. Il prend un paramètre, la taille de la mémoire à allouer en octets, et renvoie un pointeur vers la mémoire allouée.

  • fonction free() : utilisée pour libérer la mémoire précédemment allouée. Il prend comme argument un pointeur vers la mémoire à libérer et marque cette mémoire comme inutilisée.

  • Fonction calloc() : utilisée pour allouer dynamiquement de la mémoire et l'initialiser à zéro. Il prend deux paramètres, le nombre de blocs de mémoire à allouer et la taille de chaque bloc de mémoire en octets, et renvoie un pointeur vers la mémoire allouée.

  • Fonction realloc() : utilisée pour réallouer de la mémoire. Il prend deux arguments, un pointeur précédemment alloué et une nouvelle taille de mémoire, puis essaie de redimensionner le bloc de mémoire précédemment alloué. Il renvoie un pointeur vers la mémoire réallouée si l'ajustement a réussi, ou un pointeur nul dans le cas contraire.

  • Opérateur sizeof : utilisé pour obtenir la taille (en octets) d'un type de données ou d'une variable .

  • Opérateur pointeur : utilisé pour obtenir la valeur de l'adresse mémoire ou de la variable pointée par le pointeur.

  • & Opérateur : utilisé pour obtenir l'adresse mémoire d'une variable.

  • * Opérateur : utilisé pour obtenir la valeur de la variable pointée par le pointeur.

  • -> opérateur : utilisé pour l'accès du pointeur aux membres de la structure, la syntaxe est pointeur->membre, ce qui équivaut à (*pointeur).membre.

  • Fonction memcpy() : utilisée pour copier des données de la zone mémoire source vers la zone mémoire cible. Il prend trois paramètres, un pointeur vers la zone mémoire destination, un pointeur vers la zone mémoire source et la taille des données à copier en octets.

  • Fonction memmove() : Semblable à la fonction memcpy(), mais elle peut gérer les régions de mémoire qui se chevauchent. Il prend trois paramètres, un pointeur vers la zone mémoire destination, un pointeur vers la zone mémoire source et la taille des données à copier en octets.

Je suppose que tu aimes

Origine blog.csdn.net/w2009211777/article/details/130005302
conseillé
Classement