¿Cuáles son algunas preguntas sobre C que solo los programadores expertos de C pueden responder?

No podría responder la mayoría de las preguntas enumeradas en otras respuestas, muchas de las cuales se encuentran bajo el código de ‘trucos’ en lugar de ‘ruta normal’. Muchas de las preguntas también dependen del compilador, que estoy menos inclinado a ver como preguntas válidas, ya que no tienen una respuesta ‘correcta’.

Las cosas que solían causarme problemas:

  • Invocar funciones mediante un puntero a la función y obtener y establecer parámetros con esos punteros de función correctamente.
  • Trabajando con varargs para listas de argumentos variables.
  • Macros y código para garantizar cierta alineación de datos, por ejemplo, en un límite de 64 bytes.
  • Uso de punteros dobles correctamente, especialmente cosas como punteros dobles a matrices bidimensionales.

Los trucos son buenos y buenos, pero creo que es más significativo preguntar sobre los comportamientos que se usarían en el código de producción.

Estoy siguiendo los pasos de Barry Rountree, haciendo preguntas para ver quién puede resolver algunas preguntas difíciles que surgen en situaciones de programación en C. Tenga en cuenta que estoy considerando “C en la práctica” en lugar de “C como lo define el estándar”. Estos son dos idiomas diferentes. Como Barry me otorgó varios créditos por resolver sus preguntas difíciles, también les otorgaré una recompensa de crédito.

[ EDITAR : Ver más abajo. He agregado algunas nuevas preguntas difíciles.]

  1. (500 créditos: Resuelto. Vea el comentario de Sepehr Sameni a continuación, especialmente sobre la importancia de los puntos de secuencia ) .

    En el código fuente de DOOM, sí, ese DOOM, por idSoftware, se ve el idioma P_Random()-P_Random() para generar un número aleatorio firmado con una distribución triangular.

    ¿Qué preocupación de portabilidad plantea este modismo y por qué? ¿Cómo solucionaría esto y por qué funciona?

    (Aquí hay un ejemplo de ese idioma en el código fuente de DOOM: @https: //github.com/id-Software/D…)

  2. (500 créditos: Resuelto. Vea el comentario de Stéphane Zuckerman a continuación para conocer la gran restrict ganancias que ofrece ) .

    ¿Cuál es el papel de la palabra clave restrict C99? Dé un ejemplo concreto de su eficacia, no solo una reiteración de su definición.

  3. (1k créditos: Resuelto. Vea los comentarios de Vignesh Kannan a continuación. Todavía me encantaría saber de los usos prácticos reales que la gente ha encontrado para los símbolos débiles ) .

    ¿Qué es un símbolo débil y por qué usaría uno? Da un ejemplo concreto.

Haría más preguntas difíciles y / o asignaría recompensas más altas a algunos, pero todavía no tengo muchos créditos para otorgar.


Ok, entonces he decidido agregar algunas preguntas nuevas y difíciles.

1. (100 créditos: resuelto por Ray Doyle a continuación) La intención del siguiente código es proporcionar un búfer circular de 100 elementos. La idea es que en todo el rango de índices, obtendrá o establecerá solo uno de los 100 valores en el búfer circular, en virtud del direccionamiento del módulo.

Sin embargo, este código falla o produce resultados incorrectos en la mayoría de los sistemas cuando el índice es negativo. ¿Por qué?

#definir N (100)
int circular_buffer [N];

int get_from_circular_buffer (int index)
{
return circular_buffer [índice% N];
}

void put_in_circular_buffer (int index, int value)
{
circular_buffer [índice% N] = valor;
}

Repase su solución con un ejemplo concreto que demuestre el comportamiento erróneo. Explique qué garantía del lenguaje C lleva a este comportamiento.

2. (400 créditos: resuelto por Ray Doyle a continuación) Con base en la respuesta a la pregunta anterior, ¿qué implicación tiene esto en la relación entre estas dos expresiones? ¿Cómo se relaciona esto con la pregunta anterior? Proporcione un ejemplo concreto de dónde expresión_1 y expresión_2 terminan con resultados diferentes. (Debe responder a ambas partes y proporcionar un ejemplo concreto para los 400 créditos).

int x;
// supongamos que algunos conjuntos de códigos ‘x’ tienen un valor significativo aquí
int expresión_1 = x / 2;
int expresión_2 = x >> 1;

Nota: Soy consciente de que C no requiere implementaciones para firmar los cambios a la derecha extendidos. Deja esta implementación definida. Suponga que la implementación firma extender los desplazamientos a la derecha, de modo que desplazar a la derecha un número negativo produce un número negativo. Esta pregunta trata más sobre el comportamiento de la división que sobre el comportamiento del cambio.

3. (100 créditos: Resuelto por Ray Doyle a continuación.) ¿Cuál es la diferencia entre estas dos expresiones?

flotador a, b;
// supongamos que algunos conjuntos de códigos ‘a’ y ‘b’ tienen valores significativos aquí
expresión flotante_1 = 1.0 + a + b;
expresión_2 flotante = 1.0f + a + b;

4. (400 créditos: Resuelto por Ray Doyle a continuación) ¿ Cuándo darían resultados diferentes las expresiones de la pregunta anterior?

Proporcione un ejemplo concreto.

5. (200 créditos: Resuelto por Peter Poon a continuación.) ¿Por qué esta afirmación if nunca ejecuta su cláusula ‘entonces’? Suponga que x es de tipo int .

si (x & 1 == 0)
printf (“x =% d es par \ n”, x);

Dicho de otra manera, ¿por qué este programa no produce resultados?

#include

int main ()
{
int x;

para (x = 0; x <1000; x ++)
si (x & 1 == 0)
printf (“x =% d es par \ n”, x);

devuelve 0;
}

Han pasado años desde que escribí suficiente C para darme cuenta, pero las preguntas aquí me parecen insuficientemente consideradas y me siento obligado a contribuir con algunas que al menos están relacionadas con C en lugar de temas como el lenguaje ensamblador x86.

1. ¿Qué es una “definición tentativa”? ¿Cómo difiere de una definición? Mostrar al menos una definición que no sea una definición tentativa.

2. ¿Qué es un “tipo compuesto”? ¿En qué se diferencia el “tipo compuesto” del “tipo compatible”?

3. ¿A qué se refiere “vinculación”? ¿Cuáles son las tres clases de vinculación? ¿Qué significa cada uno? ¿Qué palabras clave dan qué vinculación? Nombra al menos un caso en el que las palabras clave que producirían diferentes enlaces se pueden aplicar al mismo objeto, y especifica el enlace que se produciría en ese caso.

4. Especifique una circunstancia bajo la cual una operación aritmética pueda producir un resultado que tenga un tipo diferente del tipo de cualquiera de sus operandos.

Originalmente pensé que era mejor evitar las preguntas de “¿qué hace este código?” tipo, pero después de pensarlo decidí agregar uno de esos también. Esto es realmente mucho más una prueba de persistencia que experiencia (es decir, cualquier persona que conozca C debería poder resolverlo si está dispuesto a pasar un poco de tiempo en ello), pero de todos modos es algo divertido. Entonces, el desafío es: averiguar qué salida produce esto, por su cuenta (sin compilarlo ni ejecutarlo):

#include

char * c [] = {“ENTER”, “NUEVO”, “PUNTO”, “PRIMERO”};
char ** cp [] = {c + 3, c + 2, c + 1, c};
char *** cpp = cp;

principal()
{
printf (“% s”, ** ++ cpp);
printf (“% s”, * – * ++ cpp + 3);
printf (“% s”, * cpp [-2] +3);
printf (“% s \ n”, cpp [-1] [- 1] +1);
devuelve 0;
}

La complejidad de las expresiones está destinada a (y creo que tiene éxito) progresar desde bastante simple para la primera llamada `printf`, a sustancialmente más compleja para la última.

El crédito (¿culpa?) Por este código va a Thad Smith.

Cuanto más se aprende, más saben lo que no saben. Por lo tanto, es difícil certificar preguntas exclusivas de expertos, pero aquí hay algunas cosas que podrían hacer tropezar a los programadores experimentados de C. Por lo menos, si alguien los conoce bien, se debe considerar tener una exposición profunda a C.

a) Significado y uso de (* (nulo (*) ()) 0) ();
b) Efectos y diferencias entre las matrices estáticas inicializadas frente a las no inicializadas, por ejemplo, ¿eso afecta el tamaño del binario?
c) Enumere todas las formas de pasar una matriz 2D como un parámetro de función y poder explicarlas (Sugerencia: las matrices no son punteros)
d) Punteros de bloque
e) Definir estructuras como macros y su concatenación (aunque nunca se recomienda en el código de producción principalmente debido a la escasa legibilidad y, por lo tanto, a la mantenibilidad), pero se necesita ver una estructura utilizada en un proyecto y NO poder encontrar una definición directa de la misma. y en su lugar tener que extraerlo de una cadena de macros
f) Saber qué está haciendo este código
char a [10];
(char *) (& a + 1) – (char *) a)
((char *) (& a + 1) – (char *) & a);
g) ¿Por qué un conmutador / caja sería mejor internamente que una cadena? ¿Puede codificar un breve ejemplo que lo demuestre o saber cómo el lenguaje implementa esas características?

Puedo continuar probablemente …

Nota: Una buena lectura y quizás mencionando uno o dos de los anteriores es “trampas y trampas C”

Actualización: Tengo curiosidad por ver quién puede resolver este tipo de problemas, así que estoy ofreciendo recompensas de crédito por soluciones. Si crees que tienes una respuesta, deja tu solución en los comentarios. Si eres el primero en resolver un problema en particular y me gusta tu solución, te regalaré el número indicado de créditos. Se recomiendan soluciones tontas, pero no se otorgarán nada más que puntos de estilo.

1. (1k créditos) (Resuelto por Joe Zbiciak.) ¿Qué funciones se llaman antes de llamar a main (), qué hacen y cómo se pueden manipular?

2. (2k créditos) ¿En qué circunstancias sería apropiado usar el indicador RTLD_FIRST con dlopen ()? (Dime algo más de lo que está en la página del manual).

3. (3k créditos) (Resuelto por Joe Zbiciak, con una tercera solución de Gustavo Kasper Facenda.) ¿Para qué tipo de valores de x, y y z esta función devuelve correctamente 0? (Aquí hay al menos dos clases de solución; proporcione ambas).

int f (doble x, doble y, doble z) {
return (x + y) + z == x + (y + z);
}

4. (4k créditos) Resuma un problema donde un script de enlazador personalizado sería la mejor solución. (Resuelto por Joe Zbiciak.)

5. (10k créditos) (Resuelto por Nikita Kakuev.) ¿Por qué este código ocasionalmente causa un defecto? Suponga que todos los punteros de entrada apuntan a ubicaciones de memoria válidas. También suponga que “cpuid_leaf” y “cpuid_subleaf” son tipos de 64 bits sin signo. Como sugerencia adicional: cpuid se llama correctamente y los valores resultantes también son correctos.

vacío
cpuid (cpuid_leaf eax_leaf, cpuid_subleaf ecx_leaf, uint32_t * eax, uint32_t * ebx, uint32_t * ecx, uint32_t * edx)
{
__asm__ volátil (
“xchg %% ebx, %% edi \ n \ tcpuid \ n \ txchg %% ebx, %% edi”
: “= a” (* eax), “= D” (* ebx), “= c” (* ecx), “= d” (* edx)
: “a” (eax_leaf), “c” (ecx_leaf)
);
}

GCC es el más utilizado. Tiene algunas extensiones más allá de los estándares C. Incluso los expertos de C en algún momento pensarán dos veces para comprender estas extensiones, por ejemplo, aquí hay un caso simple. ¿Qué obtienes al llamar a foo ()? ¿por qué?

De: gcc-mirror / gcc

/ * PR c ++ / 57945 * /

/ * {compilación dg-do} * /

extern int j;

static int i __attribute __ ((weakref (“j”)));

En t

foo (vacío)

{

volver y yo? yo: 0;

}

¿Cuál es el resultado del siguiente programa?

#include
principal()
{
printf (1 + “% d”, 4);
}

La salida es
re

Explicación: Siempre que haya (algún número entero) + cadena de control como primer parámetro para printf, significa que se deben eliminar tantos caracteres de la cadena de control (desde la izquierda).
así que en este caso se elimina% de “% d” para que la cadena de control se vea como “d”

¿Qué hace esta macro?

#define container_of (ptr, type, member) ({\
const typeof (((type *) 0) -> miembro) * __ mptr = (ptr); \
(type *) ((char *) __ mptr – offsetof (type, member));})
Este no es un código de truco de salón de nerds oscuro. Se usa en muchos lugares del kernel de Linux.


¿Qué significa esta declaración?

char * (* c [10]) (int ** p);

Fuente: Amazon.com: Programación Expert C: Deep C Secrets (9780131774292): Peter van der Linden: Libros

AFAIK este es el único libro que explica claramente cómo analizar cualquier declaración C compleja sin tirar de tu cabello.

P: ¿Por qué escribimos “#” antes de incluir cualquier directiva de biblioteca, ¿qué significa “#” en “# include < library to include >”?

A: “#” significa preprocesador del lenguaje C. Todas las directivas en lenguaje C deben comenzar con “#” para que todas las bibliotecas se puedan incluir antes de que se ejecute su main () real.

Yo y varios colegas a lo largo de los años hemos llegado a llamar a este tipo de cosas “code-fu”, una especie de combate / bromas sobre el código complicado / oscuro y la capacidad de comprenderlo o generarlo.

Puede ser muy divertido y todo, pero creo que es bastante superficial. Una medida completamente diferente de la habilidad de un programador también podría ser la cantidad de este tipo de cosas que él / ella puede * evitar * mientras escribe código que ofrece una funcionalidad / capacidad intensiva.

En resumen, si puedo lograr la funcionalidad intensisve mientras mantengo las cosas simples en cuanto al código, eso podría contarse como un tipo superior de “victoria”, y posiblemente una marca de habilidad aún mayor que sondear las profundidades de la técnica complicada / oscura.

Esta dimensión de habilidad también trasciende los límites del lenguaje.

Tantas respuestas tienen la sensación de “leer mi código [buscado en Google] y ver si puedes descubrir lo que hace” que realmente tengo que admitir que la capacidad de leer el código de otros y comprenderlo puede hacerte un experto Programador en C, pero cuando enseñaba programación en C encontré otra forma.

Los punteros en C pueden causar dolor de cabeza a muchos programadores, pero una vez que los entiendes, estás en camino de convertirte en un experto programador en C. Una pregunta que muestra que ambos aprendieron sus cosas y que probablemente las aplicaron es: ¿pueden escribir una función de comparación que funcione para bsearch ()?

En primer lugar, tres preguntas increíbles que incluso los programadores de C no pueden responder

  1. ¿Quién me puede enseñar C?
  2. ¿De qué sirve C en mi carrera?
  3. por qué llamamos C como C pero no E, D, F

Ahora seamos serios.

Preguntas que solo los programadores de C pueden responder

  1. Configuración de directorio en programación C
  2. Error relacionado informado por el compilador y error real
  3. Diferencia entre
    • = y ==,
    • enfoque de arriba hacia abajo y de abajo hacia arriba
    • y por último pero no menos importante C y C ++

No soy un programador experto en C, pero apuesto a que muchos programadores principiantes en C tendrán poca confusión sobre cuándo usar uint8_t y unsigned char.

Ambos tienen el mismo tamaño si está ejecutando su programa en una arquitectura de 8 bits, pero son diferentes si está ejecutando una arquitectura de 16 o 32 bits.

Puedes aclararlo aquí.

Diferencia entre uint8_t y unsigned char, uint16_t o uint32_t y unsigned int

¿El siguiente código es portátil y está bien definido? Si es así, ¿por qué? ¿Si no, porque no?

#include

typedef struct {
Unión {
uint32_t the_int;
uint8_t los_bytes [4];
} foo;
} bar;

int main (nulo)
{
bar val;

val.foo.the_int = 15;
val.foo.the_bytes [0] = 0;
val.foo.the_bytes [3] = 0;

return val.foo.the_int;
}

¿Qué devuelve (++ x – ++ x)?

La respuesta correcta es: “No sé”. O, si quieres ser más útil: “depende de qué compilador de C y qué opciones uses”.

De vez en cuando preguntaba: ¿Qué hace este código?

char * p1, p2;
int i;
/ * … * /
para (i = 0; i <10; ++ i) {
* p1 ++ = * p2 ++;
}

Muchas preguntas parecen no ser tanto sobre C sino sobre sutiles preguntas sobre redondeo numérico y NaN y qué registra los cambios de CPUID. No es que esos no sean problemas reales, ¿pero realmente se tratan de C?

More Interesting

¿Qué algoritmos de la geometría computacional son esenciales en la programación de entrevistas?

Cómo prepararse para las entrevistas con desarrolladores Java (Spring, Hibernate)

¿Es un factor decisivo que, durante mis entrevistas en Google, cuando terminé de resolver un problema, el tiempo casi había terminado y el entrevistador no podía hacerme una segunda pregunta?

Al hacer una entrevista de codificación de pizarra y te quedas atascado, ¿está bien explicarle al entrevistador cómo usarías Google para encontrar una respuesta?

¿Es cierto que en las grandes compañías de desarrollo de software, el número promedio de líneas de código libres de errores escritas en un día por desarrollador es inferior a 10?

Si no puedo resolver problemas de algoritmos en la entrevista técnica, ¿cómo debería fallar la entrevista de una manera excelente?

Algoritmo para calcular el número de dígitos pares e impares en un número?

¿Cuál es la relevancia de hacer preguntas de algoritmos en una entrevista de trabajo?

¿Qué tipo de preguntas puede esperar que le hagan en Hacker-x? Sé que es como una cita rápida en la que obtienes 5 minutos con cada compañía, pero me gustaría saberlo.

¿Cuáles son las preguntas de entrevista más importantes para Java (núcleo y avanzado tanto)?

¿Cuáles son las preguntas que se le hacen a un ingeniero de instrumentación en una entrevista técnica?

¿Cómo debo prepararme para las entrevistas FSAE o BAJA?

¿Es Interview Cake el mejor recurso para codificar preguntas de entrevistas en 2017?

¿Las personas con experiencia se prepararán mucho para las entrevistas? En caso afirmativo, ¿qué tipo de habilidades buscan las empresas en los ingenieros superiores frente a los de primer año?

¿Cuánto cuestan las preguntas formuladas en la entrevista relacionadas con el perfil del trabajo? ¿Preguntas sobre estructuras algorítmicas y de datos especialmente?