Test C si la variable est dans la section en lecture seule

J’aimerais écrire une fonction de journalisation de bas niveau qui ressemblerait à ceci:

DO_DBG("some ssortingng", val1, val2) 

Ce que je souhaite, c’est de stocker le pointeur sur la chaîne plutôt que sur une copie de la chaîne, pour des raisons de performances. Cela suppose que la chaîne est un littéral en lecture seule. Pour éviter que les personnes aient à déboguer le débogueur, il serait bien que le compilateur puisse se plaindre si le premier paramètre de DO_DBG était dans une section inscriptible de code vs texte, etc. Je me demande s’il existe un mécanisme permettant de le faire. (J’utilise gcc 4.9.1, ld 2.24).

Vous pouvez utiliser la concaténation littérale automatique à votre avantage:

 #define DO_DBG(s, a, b) _DO_DBG("" s, a, b) 

Et implémentez votre vraie macro en tant que _DO_DBG .

Vous pouvez configurer un gestionnaire SIGSEGV , puis essayer de faire:

 s[0] = s[0]; 

Si le gestionnaire est déclenché, cela signifie que la variable est dans un segment en lecture seule. Vous pouvez donc enregistrer le pointeur. Sinon, vous pouvez vous plaindre (ou simplement faire une copie de la chaîne).

Vous devrez déclarer la chaîne volatile pour que le compilateur n’optimise pas l’affectation.

Sinon, vous pouvez utiliser la construction de macro ssortingngify:

 #define DO_DBG(s, a, b) DO_DBG_INTERNAL(# s, a, b) 

et appelez votre fonction DO_DGB_INTERNAL pour que les gens fassent:

 DO_DBG(some ssortingng, val1, val2) 

PS, très probablement une fonction variadic et macro serait une bonne idée ici.

Votre fonction obtient un pointeur sur le début de la chaîne. Si votre système d’exploitation le permet et que le compilateur le configure de cette manière, une chaîne constante (comme dans votre exemple) peut être allouée dans une zone de mémoire en lecture seule. Si votre compilateur est intelligent, il stockera une copie d’une constante de chaîne qui apparaît plusieurs fois. Il se peut même que certaines chaînes ne soient jamais utilisées et ne les stockent pas du tout.

À part ce qui précède, à moins que votre programme repose sur un comportement indéfini (échapper à l’écriture sur une chaîne constante), vous ne devriez pas être en mesure de déterminer s’il s’agit ou non d’une mémoire en lecture seule. Vous pouvez comparer les adresses de chaînes pour voir s’il ne s’agit que d’une copie, mais c’est tout.

Dans toute implémentation C raisonnable (c’est-à-dire qui ne déroge pas du tout pour vous foutre dessus), vous ne verrez aucune différence de performance (ou tout au plus une très petite) performance si la chaîne est en lecture seule, en lecture-écriture. , une copie ou plusieurs.

Si vous écrivez votre fonction avec un paramètre char * et que vous stockez ledit pointeur (pas une copie dans la chaîne pointée vers), vous devriez toujours récupérer la même chaîne (à moins que votre environnement ne soit vraiment étrange).

Voici un test basé sur le gestionnaire SIGSEGV pour vérifier si la mémoire pointée sur est mappée en lecture seule:

 #define _GNU_SOURCE #include  #include  #include  #include  static sigjmp_buf label; static void segv_hndlr(int Sig) { siglongjmp(label,1); } //safe in this ctx _Bool is_ro(char volatile *X) { _Bool r=0; struct sigaction old; X[0]; //fault now if the target isn't writable if(sigsetjmp(label,1)) { r=1; goto out; } sigaction(SIGSEGV,&(struct sigaction){.sa_handler=segv_hndlr}, &old); X[0]=X[0]; out: sigaction(SIGSEGV,&old, NULL); return r; } //example: int main() { #define PR_BOOL(X) printf(#X"=%d\n", X) char bar[]="bar"; char *baz = strdup("baz"); static int const static_ro_int = 42; int const auto_ro_int = 42; PR_BOOL(is_ro("foo")); //1 PR_BOOL(is_ro(bar)); //0 PR_BOOL(is_ro(baz)); //0 PR_BOOL(is_ro((void*)&static_ro_int)); //1 PR_BOOL(is_ro((void*)&auto_ro_int)); //0 }