significado - ¿Puede un puntero(dirección) ser negativo alguna vez?
pointers significado (12)
Tengo una función en la que me gustaría poder devolver valores especiales por falla y sin inicializar (devuelve un puntero al éxito).
Actualmente devuelve NULL para el fallo, y -1 para sin inicializar, y esto parece funcionar ... pero podría estar engañando al sistema. iirc, las direcciones son siempre positivas, ¿verdad? (Aunque dado que el compilador me permite establecer una dirección en -1, me parece extraño).
[update] Otra idea que tuve (en caso de que -1 fuera arriesgado) es malloc a char @ the global scope, y usar esa dirección como centinela.
@James es correcto, por supuesto, pero me gustaría agregar que los punteros no siempre representan direcciones absolutas de memoria, que teóricamente siempre serían positivas. Los punteros también representan direcciones relativas a algún punto de la memoria, a menudo una pila o un puntero de marco, y pueden ser tanto positivos como negativos.
Por lo tanto, lo mejor es hacer que su función acepte un puntero a un puntero como parámetro y llene ese puntero con un valor de puntero válido cuando tenga éxito al devolver un código de resultado de la función real.
Cuál es la diferencia entre el fracaso y la individualización. Si la unidad no es otro tipo de falla, entonces probablemente desee rediseñar la interfaz para separar estas dos condiciones.
Probablemente la mejor manera de hacerlo es devolver el resultado a través de un parámetro, por lo que el valor de retorno solo indica un error. Por ejemplo, donde escribirías:
void* func();
void* result=func();
if (result==0)
/* handle error */
else if (result==-1)
/* unitialized */
else
/* initialized */
Cambiar esto a
// sets the *a to the returned object
// *a will be null if the object has not been initialized
// returns true on success, false otherwise
int func(void** a);
void* result;
if (func(&result)){
/* handle error */
return;
}
/*do real stuff now*/
if (!result){
/* initialize */
}
/* continue using the result now that it''s been initialized */
El lenguaje C no define la noción de "negatividad" para punteros. La propiedad de "ser negativo" es principalmente aritmética, no aplicable en modo alguno a los valores del tipo de puntero.
Si tiene una función de devolución de puntero, entonces no puede devolver significativamente el valor de -1
de esa función. En el lenguaje C, los valores integrales (distintos de cero) no son convertibles implícitamente a tipos de punteros. Un intento de devolver -1
desde una función de devolución de puntero es una violación de restricción inmediata que dará como resultado un mensaje de diagnóstico. En resumen, es un error. Si su compilador lo permite, simplemente significa que no aplica esa restricción de manera estricta (la mayoría de las veces lo hacen por compatibilidad con el código preestablecido).
Si fuerza el valor de -1
al tipo de puntero mediante un lanzamiento explícito, el resultado del reparto será definido por la implementación. El lenguaje en sí no garantiza nada al respecto. Puede demostrar fácilmente que es lo mismo que otro valor de puntero válido.
Si desea crear un valor de puntero reservado, no es necesario malloc
nada. Puede simplemente declarar una variable global del tipo deseado y usar su dirección como el valor reservado. Se garantiza que es único.
En general, es un mal diseño intentar multiplexar valores especiales en un valor de retorno ... está tratando de hacer demasiado con un solo valor. Sería más limpio devolver su "puntero de éxito" a través de argumentos, en lugar del valor de retorno. Eso deja mucho espacio no conflictivo en el valor de retorno para todas las condiciones que desea describir:
int SomeFunction(SomeType **p)
{
*p = NULL;
if (/* check for uninitialized ... */)
return UNINITIALIZED;
if (/* check for failure ... */)
return FAILURE;
*p = yourValue;
return SUCCESS;
}
También debe hacer la comprobación típica de los argumentos (asegúrese de que ''p'' no sea NULO).
En realidad, (al menos en x86), la excepción de puntero NULL se genera no solo al desreferenciar el puntero NULL, sino también mediante un rango de direcciones más grande (por ejemplo, los primeros 65kb). Esto ayuda a detectar errores como
int* x = NULL;
x[10] = 1;
Por lo tanto, hay más direcciones que están garantizadas para generar la excepción de puntero NULL cuando se desreferencia. Ahora considere este código (hecho compilable para AndreyT):
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define ERR_NOT_ENOUGH_MEM (int)NULL
#define ERR_NEGATIVE (int)NULL + 1
#define ERR_NOT_DIGIT (int)NULL + 2
char* fn(int i){
if (i < 0)
return (char*)ERR_NEGATIVE;
if (i >= 10)
return (char*)ERR_NOT_DIGIT;
char* rez = (char*)malloc(strlen("Hello World ")+sizeof(char)*2);
if (rez)
sprintf(rez, "Hello World %d", i);
return rez;
};
int main(){
char* rez = fn(3);
switch((int)rez){
case ERR_NOT_ENOUGH_MEM: printf("Not enough memory!/n"); break;
case ERR_NEGATIVE: printf("The parameter was negative/n"); break;
case ERR_NOT_DIGIT: printf("The parameter is not a digit/n"); break;
default: printf("we received %s/n", rez);
};
return 0;
};
esto podría ser útil en algunos casos. No funcionará en algunas arquitecturas de Harvard, pero funcionará en las de von Neumann.
La respuesta de James probablemente sea correcta, pero por supuesto describe una opción de implementación , no una elección que pueda hacer.
Personalmente, creo que las direcciones son "intuitivamente" sin firmar. Encontrar un puntero que se compare como menos que un puntero nulo parecería incorrecto. Pero ~0
y -1
, para el mismo tipo de entero, dan el mismo valor. Si no está firmado de forma intuitiva, ~0
puede generar un valor de caso especial más intuitivo: lo uso bastante para las anotaciones de caso de error sin firmar. No es realmente diferente (cero es un int por defecto, por lo que ~0
es -1
hasta que lo lances) pero se ve diferente.
Los punteros en sistemas de 32 bits pueden usar los 32 bits BTW, aunque -1
o ~0
es un puntero extremadamente improbable para una asignación real en la práctica. También existen reglas específicas de la plataforma, por ejemplo, en Windows de 32 bits, un proceso solo puede tener un espacio de direcciones de 2 GB y hay un gran código que codifica algún tipo de indicador en el bit superior de un puntero (por ejemplo, para equilibrar banderas en árboles binarios equilibrados).
Los punteros pueden ser negativos, como un entero sin signo puede ser negativo. Es decir, seguro, en una interpretación de dos complementos, podría interpretar que el valor numérico es negativo porque está activado el bit más significativo.
Los valores válidos para un puntero dependen por completo de la implementación, así que, sí, una dirección del puntero podría ser negativa.
Sin embargo, lo más importante es considerar (como ejemplo de una posible opción de implementación) el caso en el que se encuentre en una plataforma de 32 bits con un tamaño de puntero de 32 bits. Cualquier valor que pueda representar ese valor de 32 bits podría ser un puntero válido. Además del puntero nulo, cualquier valor de puntero podría ser un puntero válido a un objeto.
Para su caso de uso específico, debería considerar devolver un código de estado y tal vez tomar el puntero como parámetro de la función.
NULL es el único retorno de error válido en este caso, esto es verdadero cada vez que se devuelve un valor sin signo como un puntero. Puede ser cierto que en algunos casos los puntos no serán lo suficientemente grandes como para usar el bit de signo como un bit de datos, sin embargo, dado que los punteros son controlados por el sistema operativo, no el programa no confiaría en este comportamiento.
Recuerde que un puntero es básicamente un valor de 32 bits; si este es un posible número negativo o siempre positivo es solo una cuestión de interpretación (es decir) si el 32º bit se interpreta como el bit de signo o como un bit de datos. Entonces, si interpretó 0xFFFFFFF como un número firmado, sería -1, si lo interpretara como un número sin firmar sería 4294967295. Técnicamente, es poco probable que un puntero sea tan grande, pero este caso se debe considerar de todos modos.
En cuanto a una alternativa, podría usar un parámetro de salida adicional (devolviendo NULL para todas las fallas), sin embargo, esto requeriría que los clientes creen y pasen un valor incluso si no necesitan distinguir entre errores específicos.
Otra alternativa sería utilizar el mecanismo GetLastError / SetLastError para proporcionar información de error adicional (Esto sería específico de Windows, no sé si es un problema o no) o lanzar una excepción en caso de error.
Devin Ellingson
No use malloc
para este propósito. Puede mantener la memoria innecesaria atada (si ya se usa mucha memoria cuando se llama malloc
y el centinela se asigna a una dirección alta, por ejemplo) y confunde depuradores de memoria / detectores de fugas. En su lugar, simplemente devuelve un puntero a un objeto de static const char
local static const char
. Este puntero nunca se comparará igual a cualquier puntero que el programa pueda obtener de otra manera, y solo desperdicia un byte de bss.
No, las direcciones no siempre son positivas: en x86_64, los punteros se amplían con el signo y el espacio de direcciones se agrupa simétricamente alrededor de 0 (aunque es habitual que las direcciones "negativas" sean direcciones de kernel).
Sin embargo, el punto es prácticamente irrelevante, ya que C solo define el significado de <
y >
comparaciones de punteros entre punteros que forman parte del mismo objeto o uno más allá del final de una matriz. Los punteros a objetos completamente diferentes no se pueden comparar de manera significativa excepto para la igualdad exacta, al menos en el estándar C - if (p < NULL)
no tiene una semántica bien definida.
Debe crear un objeto ficticio con duración de almacenamiento estático y usar su dirección como valor sin unintialised
:
extern char uninit_sentinel;
#define UNINITIALISED ((void *)&uninit_sentinel)
Se garantiza que tendrá una única dirección única en todo su programa.
Positivo o negativo no es una faceta significativa del tipo de puntero. Pertenecen a un entero con signo que incluye signo firmado, corto, int, etc.
La gente habla de un puntero negativo principalmente en una situación que trata la representación de la máquina del puntero como un tipo entero. por ejemplo, reinterpret_cast<intptr_t>(ptr)
. En este caso, en realidad están hablando del entero fundido. no el puntero en sí.
En algunos casos, creo que el puntero no tiene firma, hablamos de dirección en términos inferiores o superiores. 0xFFFF.FFFF
está por encima de 0x0AAAA.0000
, que es intuitivamente para los seres humanos. aunque 0xFFFF.FFFF
es en realidad un "negativo", mientras que 0x0AAA.0000
es positivo.
Pero en otros escenarios, la resta del puntero, (ptr1 - ptr2)
, da como resultado un valor firmado cuyo tipo es ptrdiff_t, es inconsistente cuando se compara con la resta del entero, signed_int_a - signed_int_b
resultado un tipo int firmado, unsigned_int_a - unsigned_int_b
produce un tipo sin firmar. Pero para el resta del puntero, produce un tipo con signo, porque la semántica es la distancia entre dos punteros, la unidad es el número de elementos.
En resumen, sugiero tratar el tipo de puntero como tipo independiente, cada tipo tiene su conjunto de operaciones en él. Para punteros (excluir el puntero de función, puntero de función de miembro y void *):
- Artículo de lista
+
, + =ptr + any_integer_type
-
, - =ptr - any_integer_type
ptr1 - ptr2
++ ambos prefijo y postfijo
- - prefijo y postfijo
Tenga en cuenta que no hay operación de / * %
para el puntero. También es compatible que el puntero deba tratarse como un tipo independiente, en lugar de "Un tipo similar a int" o "Un tipo cuyo tipo subyacente es int, por lo que debe parecerse a int".