Realmente, ví que nos meus programas fazia um typedef para criar uma string. Aí ficava igual.
Sim, o conceito é claro, confundi com meu typedef que sempre usava.
Mas sendo string um char* (o que é, se vc olha em string.h todos os parâmetros string são claro um char*), então char** é um vetor unidimensional de strings, ou ponteiro para ponteiro de char, como queira.
Modificado- Para fins de clareza, em modelagem não costumamos falar ponteiro para ponteiro. Não conheço por exemplo uma notação UML para isso (relação entre dois tipos estruturados -typedef struct para c -, não dentro de um mesmo tipo), então modelar com esse linguajar que é mais natural para a programação não fica tão atraente.
Então, a estrutura do malloc não é colocada na variável "tipo *". O que é feito na verdade pelo SO, é uma alocação na heap do sistema operacional e na variável é colocada um apontador para a posição de memória onde a memória foi alocada. Por isso é um ponteiro, porque aponta pra lá.. :p
Sim , claro. Malloc é apenas Malloc. O que disse é que eu posso ter um ponteiro de 1 byte apontando para uma estrutura de 20.
Quanto a minha alegação de 4 bytes: bem, não sei se há exceções, mas normalmente máquinas x86 tem registradores de 32 bits e o mapeamento de memória tem o mesmo tamanho... Concordo contigo quanto à isso ser variável pelo HW, tanto que fiz referência explícita pra essa arquitetura...
Sim, há exceções, que são melhores tratadas em cada sistema operacional.
Primeiro existe um mecanismo da intel chamado PAE (page address extension) que te permite isso. Tem outro recurso VLM no Red Hat AS. E tem também as características do S.O

In order to get above 4GB virtual memory on IA-32 architecture a technique known as PAE (Page Address Extensions) is used. It is a method that translates 32-bit (2**32 = 4GB) linear addresses to 36-bit (2**36 = 64GB)
Voltando ao S.O. Por exemplo, o Windows previlegia a thread. Portanto um programa servidor, que tem vários programas background, cada programa desse será uma thread no windows e a somatória dessas threads podem acessar o total de endereçamento da memória (para 32b 4GB), descontando o PAE.
O unix privilegia o processo. E cada processo que compoe o servidor vai poder utilizar o máximo endereçavel - dentro do imposto 32/64 -. Então, eu posso ter facilmente (sem PAE) uma máquina intel 32 bits com 64GB de ram e o conjunto de processos que compoe o servidor (eg um banco de dados) acessar praticamente tudo.
Com PAE, eu posso aumentar a memória que eu posso endereçar dentro do próprio processo.
Coisa do SO... Ele deve direcionar o stdout do primeiro programa para o stdin do segundo...
Aqui dei um look e parece que vc tem razão. É um exemplo, mas mostra bem o mecanismo, e não a comunicação não se dá entre as chamadas.
http://www.ee.ic.ac.uk/docs/software/unix/programming/sys/transfer/pipe.html
The following program pipes the output of the ps command in the parent to the grep command in the child to find details pertinent for the current terminal (c.f. ttyname()).
setup % cat > pipe.c
/* parent mutates into ps which pipes it output to the child which has mutated
into grep */
#include <stdio.h>
#define ENUF(msg, value) {perror(msg); exit(value); }
main(argc, argv)
int argc;
char *argv[];
{ int i, pid, fd[2];
char *gets(), *ttyname();
char tty[4], *ptr;
if ( pipe(fd) != 0) ENUF("pipe creation error", 1);
i = 0;
while ( (pid=fork()) == -1) /* try a few times before giving up */
if (++i > 5 ) ENUF("failed to form after 5 attempts", 2) else sleep(5);
if (pid == 0) { /* CHILD process */
ptr = ttyname(fileno(stdin)); tty[0] = ' '; tty[1] = ptr[strlen(ptr)-2];
tty[2] = ptr[strlen(ptr)-1]; tty[3] = ' '; tty[4] = '\0';
fprintf(stdout,
" Child pipe fds before dup: fd[0] %d, fd[1] %d\n",fd[0],fd[1]);
close(fd[1]); /* not using pipe write fd=4 */
close(0); /* redirecting stdin fd=0 */
i = dup(fd[0]); /* duplicate pipe read with stdin i.e. 0=3 */
fprintf(stderr,
" Child pipe fds after dup: fd[0] %d, fd[1] %d \n", i, fd[1]);
close(fd[0]); /* close pipe read after use fd=3 */
/* list processes running on current terminal */
if (execl("/bin/grep", "grep", tty, NULL) == -1) ENUF("Unable to run grep", 3);
}
else { /* PARENT process */
fprintf(stderr,
"Parent pipe fds before dup: fd[0] %d, fd[1] %d \n", fd[0], fd[1]);
close(fd[0]); /* not using pipe read fd=3 */
close(1); /* redirecting stdout fd=1 */
i = dup(fd[1]); /* duplicate pipe write with stdout i.e. 1=4 */
fprintf(stderr,
"Parent pipe fds after dup: fd[0] %d, fd[1] %d \n", fd[0], i);
close(fd[1]); /* close pipe write after use fd=4 */
/* list processes running on terminal in long format */
if (execlp("ps", "-lx", NULL) == -1) ENUF("Unable to run ps", 4);
}
} /* main */
% acc -w -o DEMO pipe.o
% ps -xl | grep p5
2048820110287 1666 1665 0 15 0 80 184 kernelma S p5 0:00 -h -i (csh)
2000820110287 1749 1666 2 15 0 76 404 kernelma S p5 0:00 /bin/csh -f
2000000110287 1771 1749 22 30 0 216 464 R p5 0:00 ps -xl
2000000110287 1772 1749 6 1 0 32 212 socket S p5 0:00 grep p5
E ainda tem os pipes baseados em IPC e semáforos, que não são o mesmo que o pipe do Unix, como vimos acima
modificado: cáspita, não tenho uma conversa assim há anos... Vou mudar de área de novo dentro da empresa

[ ]