Ir ao conteúdo
  • Cadastre-se

C Ponteiro de ponteiro em expressões


Lipeco

Posts recomendados

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i;
    int *ponteiro1;
    int *ponteiro2;
    int **ponteirodePonteiro;

    ponteiro1 = (int*)malloc(5*sizeof(int));
    ponteiro2 = &ponteiro1[1];
    ponteirodePonteiro = &ponteiro2;

    for(i=0; i<5;i++)
    {
        ponteiro1[i] = i;
        printf("Ponteiro1[ %i ] = %i\n",i,ponteiro1[i]);
    }

//EXPRESSÃO

ponteirodePonteiro[0][3]; //Essa expressão está correta? Esse ponteiro aponta para o endereço do ponteiro2 que aponta para o endereço do ponteiro1
    //Como eu faria para exibir essa expressão? 

    return 0;
}

Caso tenha alguns exemplos, eu agradeceria.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

@Lipeco Exibir o que? ponteirodePonteiro recebeu o endereço de ponteiro2, mas você não alocou memória pra ele, não é uma matriz dinâmica.

// Matriz dinamica
int i;
int** mat = (int**)malloc(rows * sizeof(int*));
for(i = 0; i < rows; i++) {
    mat[i] = (int*)malloc(cols * sizeof(int));
}

// Desalocando
for (i = 0; i < rows; i++) {
    free(mat[i]);
}
free(mat);

E mesmo assim

2 horas atrás, Lipeco disse:
ponteirodePonteiro[0][3];

não haveria como exibir nada sem antes inicializar ou coisa do tipo.

O que está tentando exibir?

  • Curtir 1
  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

@Lipeco   escrever o conteúdo dessa variável  "ponteirdePonteiro" é igual as outras 

ponteirodePonteiro[0][3]; // Essa expressão está correta? 
                          // Esse ponteiro aponta para o endereço 
                          // do ponteiro2 que aponta para o endereço 
                          // do ponteiro1
                          // Como eu faria para exibir essa expressão? 
printf("\nponteirodePonteiro[0][3] = %d\n",ponteirodePonteiro[0][3]);
free(ponteiro1);

 

  • Curtir 2
  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

@Lipeco A memória alocada foi pra ponteiro1, um vetor dinâmico. ponteiro2 aponta pra segunda posição do vetor, então ponteirodePonteiro[0][0] seria equivalente a ponteiro1[1], e ponteirodePonteiro[0][3] seria equivalente a ponteiro1[4].

 

28 minutos atrás, Lipeco disse:

pois nem inicializei ele

Se a memória alocada fosse pra ponteirodePonteiro, então precisaria inicializar, pensei que fosse isso que tentava fazer.

De certa forma, não precisa de ponteirodePonteiro, você pode fazer um ponteiro apontar pra mesma posição de memória que ponteiro1 ou ponteiro2 sem problemas, vai conseguir acessar os valores da mesma forma.

  • Curtir 1
  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

7 horas atrás, Lipeco disse:
//EXPRESSÃO

ponteirodePonteiro[0][3]; //Essa expressão está correta? Esse ponteiro aponta
para o endereço do ponteiro2 que aponta para o endereço do ponteiro1
    //Como eu faria para exibir essa expressão? 

 

 

Está no limite de errado e acho que não deve ser usado no mundo real de empregos e diplomas, pelo risco de perder o primeiro ou não conseguir o segundo 🙂. Pode ser uma questão de entrevista de emprego talvez.

 

  • É uma expressão e vai como toda expressão valer 0 ou 1 conforme o valor do elemento seja 0 ou qualquer outra coisa. Se for 0 é falso e a expressão vale 0. Para qualquer outro valor vai valer 1.

 

Mas como nada fez com isso não vai a lugar nenhum mesmo.

  • Evite comentários com acentos. Não acrescentam muito e podem não sair na tela ou na impressão.
     
  • Não faça sua pergunta dentro de um comentário de modo a forçar quem quer te ajudar a ficar procurando a sua pergunta ou a sua dúvida. Ajude os outros a ajudarem você [Minha opinião, não represento o forum]. Faça uma pergunta explícita e objetiva e use um título de acordo, de modo a facilitar que outros com dúvidas similares cheguem a esse tópico.
     

De volta ao código

 

  • Evite nomes gigantes assim para variáveis. ponteirodePonteiro? 18 letras? sério? tente por isso em uma expressão e logo terá linhas com mais de 100 colunas. p1, p2, pInt, ppInt são nomes consagrados para essas coisas em programas de teste.
  • declare variáveis de controle do loop DENTRO do loop. Levou uns anos para arrumarem isso na linguagem, mas já faz mais de 40 anos...
     
        int   i;
    // ...
        for (i = 0; i < 5; i++)
        {
            ponteiro1[i] = i;
            printf("Ponteiro1[ %i ] = %i\n", i, ponteiro1[i]);
        }

    Isso é um desastre: a variável com o ingênuo nome de i é global à função e continua válida fora do loop. Isso leva a erros chatos de encontrar. Nunca faça isso. Não use nada global. Prefira o simples
     

        for (int i = 0; i < 5; i++)
        {
            ponteiro1[i] = i;
            printf("Ponteiro1[ %i ] = %i\n", i, ponteiro1[i]);
        }

     

Em C um ponteiro é um tipo como qualquer outro. Nada tem de especial em um ponteiro de ponteiro. Só existe ponteiro para algum tipo no final das contas. Não existe ponteiro. Só existe ponteiro para algo e o ultimo asterisco tem que apontar para algo, um typedef ou um tipo qualquer. No seu caso um int.

 

De seu código

 

    int   i;
    int*  ponteiro1;
    int*  ponteiro2;
    int** ponteirodePonteiro;

    ponteiro1          = (int*)malloc(5 * sizeof(int));
    ponteiro2          = &ponteiro1[1];

 

Esqueça a primeira declaração pelas razões que já expliquei.

 

Sobre o tal ponteiro1, pelas razões que já expliquei. Prefira
 

        int* ponteiro1 = (int*) malloc(5 * sizeof(int));

 

Porque? É muito mais simples de ler e evita a existência, por uma linha que seja, de um ponteiro não inicializado. E prefira, pelas razões que já expliquei:
 

    int* p1 = (int*) malloc(5 * sizeof(int));

 

ponteiro1 é um ponteiro para int e aqui passa a apontar para uma área de 20 bytes numa máquina comum, já que um int tem em geral 4 bytes.

 

Pela mesma razão prefira

 

    int*    ponteiro2          = ponteiro1 + 1;

 

porque ponteiro1 é um endereço de um int. E o tipo de ponteiro2 é int*. E  usar & e [] só complica as coisas.  É muito mais natural e perto da máquina do que escrever
 

    ponteiro2          = &ponteiro1[1];

 

misturando as notações de array e ponteiro. p1[0] até p1[4] estão ali na área retornada por malloc(), um depois do outro. Tudo em programas é base e deslocamento.

EXEMPLO

 

    int* p1 = (int*) malloc(5 * sizeof(int));
    int* p2 = p1 + 1;
    *p2 = 300;
    int**       ppInt = &p2;

 

Está declarando ppInt, um nome, como sendo de um tipo.

  • Então ppInt é int**. Por isso deixei um espaço enorme no exemplo. E então
    • *ppInt é int*
    • **ppInt e claro um int. Isso é C
    • se *p2 é 300 então **ppInt é 300 porque apontam para o mesmo endereço (p1 + 1)

De volta a 
 

    ponteirodePonteiro[0][3];  

 

Vamos tentar algo perto desse exemplo

 

Considere um vetor então de meia dúzia desses **int
 

    int** vppInt[2][3] = { NULL,NULL,NULL,NULL,NULL,NULL };
    vppInt[0][3] = ppInt;

 

UM EXEMPLO nesse contexto

 

O programa abaixo usa essas coisas. Rode em sua máquina e veja se entende o mecanismo e escreva de volta

 

Saída do exemplo

 

Vetor logo depois de alocado
Vetor: [ 0, 0, 0, 0, 0]

o segundo elemento deve ser 300...
Vetor: [ 0, 300, 0, 0, 0]

**ppInt vale 300
Ultimo elemento agora vale 42
Vetor: [ 0, 300, 0, 0, 42]

**ppInt (agora p1[4]) vale 42
vppInt[0][3] vale 42
Penultimo elemento agora vale -42?
Vetor: [ 0, 300, 0, -42, 42]

 

EXEMPLO

 

#include <stdio.h>
#include <stdlib.h>

int mostra( const int*, const int, const char*);

int main()
{
    int* p1 = (int*) malloc(5 * sizeof(int));
    // agora p1 aponta para 5int em 20 bytes
    mostra( p1,5,"Vetor logo depois de alocado");
    int* p2 = p1 + 1;
    // agora p2 aponta ara o segundo int

    *p2 = 300;
    mostra( p1,5,"o segundo elemento deve ser 300...");

    int**    ppInt = &p2;
    printf( "**ppInt vale %i\n", **ppInt);

    p1[4] = 42;
    mostra( p1,5,"Ultimo elemento agora vale 42");
    *ppInt = p1+4;
    printf( "**ppInt (agora p1[4]) vale %i\n", **ppInt);


    int** vppInt[2][3];
    vppInt[0][3] = ppInt;
    printf( "vppInt[0][3] vale %i\n", **vppInt[0][3]);

    *( *vppInt[0][3] - 1 )  = -42;
    mostra( p1,5,"Penultimo elemento agora vale -42?");

    free(p1); // apaga a area alocada
    return 0;
}

int mostra( const int* v, const int n, const char* tit)
{
    if ( v == NULL ) return -1; // sem vetor
    // mostra titulo se veio
    if ( tit != NULL ) printf("%s\n",tit);
    // mostra o vetor
    printf("Vetor: [ %i", v[0]);
    for (int i = 1; i < n; i+=1)
        printf(", %i", v[i]);
    printf("]\n\n");
    return 0;
}

 

Em especial procure entender porque isso funciona:
 

    int** vppInt[2][3];
    vppInt[0][3] = ppInt;
    printf( "vppInt[0][3] vale %i\n", **vppInt[0][3]);

    *( *vppInt[0][3] - 1 )  = -42;
    mostra( p1,5,"Penultimo elemento agora vale -42?");

 

e estará no bom caminho de programar em C. Ou Assembler. Tudo é base (um endereço na memória) e deslocamento (um número).

 

 

  • Curtir 1
  • Obrigado 2
Link para o comentário
Compartilhar em outros sites

11 horas atrás, Lipeco disse:
//Essa expressão está correta?
Esse ponteiro aponta para o endereço do ponteiro2 que aponta para o endereço do ponteiro1
//Como eu faria para exibir essa expressão? 

 

 

Essa expressão está correta? Sim porque a notação está correta, sabemos que notação de ponteiros é trocável por vetor.

Um dublo ponteiro é o tipo que guardar o endereço de um ponteiro, se desreferenciar ele poderá ler seu conteúdo: o endereço de um ponteiro.

 

Se declarar:

int **ponteiros =  (int * [4]) {NULL};

Alocará da pilha 4 ponteiros vizinhos (vetor de ponteiros) e atribuirá a 'ponteiros' o primeiro endereço.

 

Se declarar:

 int  *_0inteiros =    (int [5]) {0};
 int  *_1inteiros =    (int [5]) {1};
 int  *_2inteiros =    (int [5]) {2};
 int  *_3inteiros =    (int [5]) {3};

Alocará da pilha, sequencialmente, 4 vetores (int) e 4 ponteiros e atribuirá o endereço respectivamente.

 

Agora tenho dois tipos derivados de int: 1 ponteiros e 4 inteiros, continue...

ponteiros[0] = _0inteiros;
ponteiros[1] = _1inteiros;
ponteiros[2] = _2inteiros;
ponteiros[3] = _3inteiros;

Quando desreferenciar 'ponteiros' terá acesso a uma das 4 variáveis do vetor, no casso acima,  é atribuído o endereço dos _Xinteiros de 0 á 3.

Em seguida poderá gravar ou ler valores nas 'inteiros'. A assinatura [Y][X] acessa um dos endereços na '_Xinteiros' conforme os valore de Y, X  e converte em variável do tipo int.

 

 

Exemplo:

#include <stdio.h>
#include <stdlib.h>
int
main(){
  int **ponteiros =  (int * [4]) {NULL};

  int  *_0inteiros =    (int [5]) {0,1,2,3,4};
  int  *_1inteiros =    (int [5]) {1,2,3,4,5};
  int  *_2inteiros =    (int [5]) {2,3,4,5,6};
  int  *_3inteiros =    (int [5]) {3,4,5,6,7};


  ponteiros[0] = _0inteiros;
  ponteiros[1] = _1inteiros;
  ponteiros[2] = _2inteiros;
  ponteiros[3] = _3inteiros;

  for(int i = 0; i < 4; ++i){
	for(int j = 0; j < 5; ++j){
		printf (" %d ", ponteiros[i][j]);
		}
	putchar('\n');
	}    
  printf("ponteiros[0][3]: %d\n", ponteiros[0][3]);
}

image.png.3e99a40f3b68d7e323b4bcbabb521df0.png

 

 

O mesmo exemplo e com argumento de função:

#include <stdio.h>
#include <stdlib.h>
void print(int **m, int x, int y);
int
main(){
  int **ponteiros =  (int * [4]) {NULL};

  int  *_0inteiros =    (int [5]) {0,1,2,3,4};
  int  *_1inteiros =    (int [5]) {1,2,3,4,5};
  int  *_2inteiros =    (int [5]) {2,3,4,5,6};
  int  *_3inteiros =    (int [5]) {3,4,5,6,7};


  ponteiros[0] = _0inteiros;
  ponteiros[1] = _1inteiros;
  ponteiros[2] = _2inteiros;
  ponteiros[3] = _3inteiros;

  print (ponteiros, 5, 4);

  printf("ponteiros[0][3]: %d\n", ponteiros[0][3]);
}
void
print (int **m, int x, int y){
  for(int i = 0; i < y; ++i){
	for(int j = 0; j < x; ++j){
		printf (" %d ", m[i][j]);
		}
	putchar('\n');
	}
  return;    
}

image.png.3e99a40f3b68d7e323b4bcbabb521df0.png

 

Observe o mesmo exemplo com uso da malloc

#include <stdio.h>
#include <stdlib.h>
void print(int **m, int x, int y);
int
main(){
  int **ponteiros =  malloc (sizeof(int * [5]));

  int  *_0inteiros =    (int [5]) {0,1,2,3,4};
  int  *_1inteiros =    (int [5]) {1,2,3,4,5};
  int  *_2inteiros =    (int [5]) {2,3,4,5,6};
  int  *_3inteiros =    (int [5]) {3,4,5,6,7};


  ponteiros[0] = _0inteiros;
  ponteiros[1] = _1inteiros;
  ponteiros[2] = _2inteiros;
  ponteiros[3] = _3inteiros;

  print (ponteiros, 5, 4);

  printf("ponteiros[0][3]: %d\n", ponteiros[0][3]);
  free (ponteiros);
}
void
print (int **m, int x, int y){
  for(int i = 0; i < y; ++i){
	for(int j = 0; j < x; ++j){
		printf (" %d ", m[i][j]);
		}
	putchar('\n');
	}
  return;    
}

image.png.1911cdadc4e4152a701edfdca0322e45.png

Ops! Algo deu errado.

 

Os compiladores modernos são escritos por programadores de altíssimo desempenho só os melhores da categoria, PhD, engenheiros, homens e mulheres, jovens que se dedicaram/dedicam-se a Normal da Linguagem de Programação C, fazem esse excelente trabalho na implementação de rotinas de verificação de sintaxe, alimentadas com os erros/falhas mais comuns objetivando educar os seus adeptos com boas práticas.

 

Nessa falha o compilador afirma que existe a hipótese' dereference '(desreferenciar) ponteiros NULL, de fato mesmo que pequena.

Logo, é necessário verificar o retorno da malloc, ou ignorar o aviso! Mas, lembre-se que os compiladores são escritos por programadores que possuem a mais elevada prática da linguagem de programação. Logo...

Vamos seguir o compilador, agora e sempre!

#include <stdio.h>
#include <stdlib.h>
void print(int **m, int x, int y);
int
main(){
  int **ponteiros =  malloc (sizeof(int * [5]));
  if(ponteiros != NULL){

  	int  *_0inteiros =    (int [5]) {0,1,2,3,4};
  	int  *_1inteiros =    (int [5]) {1,2,3,4,5};
  	int  *_2inteiros =    (int [5]) {2,3,4,5,6};
  	int  *_3inteiros =    (int [5]) {3,4,5,6,7};


  	ponteiros[0] = _0inteiros;
  	ponteiros[1] = _1inteiros;
  	ponteiros[2] = _2inteiros;
  	ponteiros[3] = _3inteiros;

  	print(ponteiros, 5, 4);

  	printf("ponteiros[0][3]: %d\n", ponteiros[0][3]);
  	free(ponteiros);
	return 0;
  	}
  puts("FAIL ponteiros es NULL!!!");
  return 1;
}
void
print(int **m, int x, int y){
  for(int i = 0; i < y; ++i){
	for(int j = 0; j < x; ++j){
		printf (" %d ", m[i][j]);
		}
	putchar('\n');
	}
  return;    
}

image.png.5449b5be5c0566a6b2dedbfcbf907a02.png

 

Por fim, represento seu experimento, esse que tem na sua postagem original, e respondo todas as perguntas de uma só vez.

#include <stdio.h>
#include <stdlib.h>
int
main(){
  int **ponteiros =  malloc (sizeof(int * [5]));
  if(ponteiros != NULL){
	int  *_1inteiros =    (int [5]) {1,2,3,4,5};
	int  *_2inteiros =    NULL;
	
	_2inteiros = &_1inteiros[1];
	free (ponteiros);

	ponteiros = &_2inteiros;
  	(void)_2inteiros;

  	printf("ponteiros[0][3]: %d\n", ponteiros[0][3]);
	return 0;
	}
  puts("FAIL es NULL");
  return 1;
}

image.png.9eca256eb8ec3267bec277a15e6873eb.png

 

A variável tem valor 5 o experimento da expressão é SUCESSO!

 

Essa expressão está correta? Sim!
Esse ponteiro aponta para o endereço do ponteiro2 que aponta para o endereço do ponteiro1? Sim, os endereços são vizinhos (no vetor inteiros)
Como eu faria para exibir essa expressão? Como escrevi!
Se ainda tem dúvida, pergunte.

 

[🙂] — espero que ajude.

  • Curtir 2
  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

Por exemplo, tenho como exercício, com esses valores setados, eu tenho que exibir depois uma lista de expressões, como abaixo:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i;
    int *ponteiro1;
    int *ponteiro2;
    int **ponteirodePonteiro;
  	
  	int valor = 10;

    ponteiro1 = (int*)malloc(5*sizeof(int));
    ponteiro2 = &ponteiro1[1];
    ponteirodePonteiro = &ponteiro2;

    for(i=0; i<5;i++)
    {
        ponteiro1[i] = i;
        printf("Ponteiro1[ %i ] = %i\n",i,ponteiro1[i]);
    }
	
    valor = ponteiro2[1]; //Esse valor passa a receber o ponteiro2 na posicao 1.
 	exibeExpressao(ponteiro1,ponteiro2,ponteiro3,valor);
}
                      
              

 Aí criei uma função para exibir as expressões:

 

void exibeExpressao(int *ptr1, int *ptr2, int **ptr3, int valor)
{
    system("cls");

    printf("\n\tEXIBIR EXPRESSÕES:\n\n");


    int a  = ptr3[0][2];
    int b = ptr1[4];
  	int c = **ptr3 + 3;
  	int d = ptr2 + ptr1[1] - 5;


    printf("  A) ponteiro3[0][2]:\t[%i][%i]\n", a);// 
    printf("  B) ponteiro1[4]:\t[%i]\n", b); // TRATAR MEMORIA não INSERIDA
	printf("  C) **ponteiro3 + 3 =\t %i \n", **ptrp + 3);
  	printf("  D)  %X \n", d); 
  
    free(ptra);

    system("\tpause");

}

 Essa lista eu tenho que exibí-la com seus resultados, porém nos casos da expressão acessar uma memória não alocada, eu tenho que imprimir uma mensagem dizendo o erro. Na expressão b por exemplo, o ponteiro1 está acessando o índice 4, nesse caso seria memória não alocada, certo? Alguma dica de como fazer esse exemplo? No exemplo d, seria um print de endereço, está correto?

 

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

3 horas atrás, Lipeco disse:

nos casos da expressão acessar uma memória não alocada, eu tenho que imprimir uma mensagem dizendo o erro

 

Não funciona assim. Não há como determinar isso. Um ponteiro é só isso: um endereço e um tipo. O que pode acontecer é cancelar seu programa simplesmente. 

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

6 horas atrás, Lipeco disse:

@arfneto  Mas eu posso criar um IF, caso o ponteiro X acessar um limite não disponível, eu colocar um printf? Por exemplo, se o B = ptr1[6]; Eu crio um if, caso ptr[i] ultrapassar 5, eu imprimo algo na tela. Seria bacana?

 

Não. Se você usar um ponteiro e tentar acessar um endereço fora do espaço alocado ___ muitas vezes chamado de rogue pointer na literatura ___ o normal é seu programa cancelar por erro de segmentação SIGSEGV no Linux/Unix/Mac/Android ou algum crash mesmo no Windows. Ou mostrar algum resultado se tiver acesso ao endereço mas algo que não foi gerado pelo seu programa.

 

Controlar o acesso à memória é sua responsabilidade em C. Há ferramentas para ajudar, e hardware também. Coisas como address sanitizers e Valgrind. Essa linguagem foi escrita para escrever sistemas com eficiência e sem ficar no caminho do desenvolvedor. E não fica. Todos os sistemas modernos e alguns extintos foram escritos em C.

  • Curtir 1
  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

Em 12/03/2022 às 14:53, Lipeco disse:

 Essa lista eu tenho que exibí-la com seus resultados, porém nos casos da expressão acessar uma memória não alocada, eu tenho que imprimir uma mensagem dizendo o erro. Na expressão b por exemplo, o ponteiro1 está acessando o índice 4, nesse caso seria memória não alocada, certo? Alguma dica de como fazer esse exemplo? 

É impossível, pois o ponteiro (sendo uma variável) tem um número tal qual uma variável do tipo inteiro. Para impedir que acesse, é necessário pelo menos mais um ponteiro e com o número desse limite. Desse modo, um ponteiro é o início (a_) e o outro o fim (u_), note haver necessidade de modifique a expressão para duplo ponteiro.

Exemplo.

#include<stdio.h>
#include<stdlib.h>
void exibir_numeros (int **nums);
int
main(void){
  int  *a_ = (int [5]) {1,2,3,4,5};
  int  *u_ = a_ + 4;

  int **nums = (int * [2]) {a_, u_};
  exibir_numeros(nums);
  return 0;
  }
void
exibir_numeros(int **nums){
  int *x = nums[0];
  while (x <= nums[1]){
	printf(" %d ", x++[0]);
	}
  if (x > nums[1]){
	printf("\nx: %p e maior que u_: %p\n", (void*)x, (void*)nums[1]);
  	}
  return;
  }

Sabe que nums[0] tem o mesmo endereço que em a_ e nums[1] o mesmo que u_.

A expressão de controle do “loop” (while) testa se o endereço na x é menor_ou_igual _a nums[1], quando diferente interrompe o “loop” impedindo acesso fora do alocado.

 

O ponteiro que tem o endereço de memória não alocado ainda não acessa esse local se não desreferenciar.

 

 

Em 12/03/2022 às 14:53, Lipeco disse:

No exemplo d, seria um print de endereço, está correto?

 Quase! Mas, use o especificado ("%p") designado para imprimir pointes e realize o cast para void *.

 

Se ainda tem dúvida, pergunte.

 

🙂 — Dica final: ative os alerta do compilador,

lembre-se, ele foi/é desenvolvido com ajuda da Elite da Programação.

  • Curtir 1
  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

6 horas atrás, mauro_b disse:

Para impedir que acesse, é necessário pelo menos mais um ponteiro e com o número desse limite. Desse modo, um ponteiro é o início (a_) e o outro o fim (u_), note haver necessidade de modifique a expressão para duplo ponteiro

 

Cuidado: isso não vai impedir o acesso: é apenas uma convenção em seu programa passar o endereço de início e o de fim.
 

6 horas atrás, mauro_b disse:

Elite da Programação.

 

A Elite da Programação criou o protótipo de main() não por acaso como sendo
 

    int main( int argc, char* argv[] );

 

muitas vezes declarado como
 

	int main( int argc, char** argv );

 

E a norma que rege TODAS as implementações de compilador C diz que argv[argc] deve ser NULL, em 5.1.2.2.1 porque assim é mais seguro.

 

Ou seja, um vetor de strings terminados por NULL daria talvez um pouco mais de solidez ao seu programa

 

De seu código

 

void exibir_numeros(int** nums)
{
    int* x = nums[0];
    while (x <= nums[1]) { printf(" %d ", x++[0]); }
    if (x > nums[1])
    {
        printf(
            "\nx: %p e maior que u_: %p\n", (void*)x,
            (void*)nums[1]);
    }
    return;
}

 

É uma simples convenção e não há qualquer garantia de acesso.

 

Você declarou

 

    int* a_ = (int[5]){1, 2, 3, 4, 5};
    int* u_ = a_ + 4;
    int** nums = (int* [2]){a_, u_};


Se chamar

 

	exibir_numeros(&a_);

 

vai compilar e rodar e mostrar provavelmente uns números na tela até cancelar. Ou vai rodar e mostrar 

 

 1  2  3  4  5 

x: 0x7fffcd182924 e maior que u_: 0x7fffcd182920

 

Dependendo do que tiver na memória no momento...

 

Não há qualquer garantia ou maneira de impedir o acesso.

 

@mauro_b apenas introduziu uma convenção e com valores constantes declarados em main() em um programa de exemplo.

 

Mudando 1 linha em exibir_numeros() e passando a usar nums[100] como argumento no if

 

void exibir_numeros(int** nums)
{
    int* x = nums[0];
    while (x <= nums[1]) { printf(" %d ", x++[0]); }

    if (x < nums[100])
        printf(
            "\nx: %p e maior que u_: %p\n", (void*)x,
            (void*)nums[1]);
    else
        printf("\n");

    return;
}

 

Não há como garantir nada sobre o ponteiro nums. Apenas no seu exemplo é passado um vetor estático de dois ponteiros, mas na função o compilador não tem como saber isso. 
 

Por isso o código compila perfeitamente e vai rodar ok  em alguns casos, dependendo do que se encontre no momento em nums[100] que é int* assim como x. Se x for maior que  nums[100] vai prontamente mostrar.

 

Claro que x não é necessário no programa já que é nums[0].
 

 1  2  3  4  5 

x: 0x7fffdca486e4 e maior que u_: 0x7fffdca486e0

 

Ou vai mostrar apenas os 5 números.

 

Note que eu mudei o if na função para garantir uma nova linha ao final mesmo que a condição no if seja falsa, parfa não sair grudado ao final com o prompt.

 

Mas...

 

Se por acaso o ponteiro tiver um endereço inacessível o programa vai cancelar por erro de segmentação:

 

Clube@DSK:~/projects/size0$ gcc  -Wall -pedantic v0.c
Clube@DSK:~/projects/size0$ ./a.out
Segmentation fault (core dumped)
Clube@DSK:~/projects/size0$ 

 

Isso vai depender provavelmente do tamanho do stack alocado pelo sistema para o programa. /Stack: no visual Studio por exemplo, onde o padrão é de modesto 1MB.

 

Sugiro uma convenção mais segura, e usar um vetor de ponteiros null-terminated, como a "Elite da Programação"  como o Sr. @mauro_b chama, optou por usar em main().

 

Mas nesse caso aqui pode ser mais simples usar apenas o que parece estar usando: o endereço inicial e o final, algo como

 

int exibir_numeros(int* ini, int* fim)
{
    if (ini == NULL) return -1;
    if (fim == NULL) return -1;
    printf("\nMostra valores de 0x%p a 0x%p (inclusive)\n", ini, fim);
    if (ini > fim)
    {
        printf("\nx: 0x%p e maior que u_: 0x%p\n", ini, fim);
        return -1;
    }
    int* p = ini;
    printf("Valores: [");
    for (int* p = ini; p <= fim; p += 1) printf(" %d", *p);
    printf(" ]\n");
    return 0;
}

 

Que para esse código

 

int  main(void)
{
    int*  de   = (int[5]){1, 2, 3, 4, 5};
    int*  ate  = de + 3;

    exibir_numeros(de, ate);
    exibir_numeros(ate, de); // tenta ao contrario
    return 0;
}

 

Mostra

 

Mostra valores de 0x059FF964 a 0x059FF970 (inclusive)
Valores: [ 1 2 3 4 ]

Mostra valores de 0x059FF970 a 0x059FF964 (inclusive)

x: 0x059FF970 e maior que u_: 0x059FF964

 

O programa completo

 

#include <stdio.h>
int exibir_numeros(int*, int*);
int main(void)
{
    int* de  = (int[5]){1, 2, 3, 4, 5};
    int* ate = de + 3;

    exibir_numeros(de, ate);
    exibir_numeros(ate, de);  // tenta ao contrario
    return 0;
}
int exibir_numeros(int* ini, int* fim)
{
    if (ini == NULL) return -1;
    if (fim == NULL) return -1;
    printf("\nMostra valores de 0x%p a 0x%p (inclusive)\n", ini, fim);
    if (ini > fim)
    {
        printf("\nx: 0x%p e maior que u_: 0x%p\n", ini, fim);
        return -1;
    }
    int* p = ini;
    printf("Valores: [");
    for (int* p = ini; p <= fim; p += 1) printf(" %d", *p);
    printf(" ]\n");
    return 0;
}

 

Mas não há qualquer garantia de que os endereços passados para exibir_numeros() sejam válidos.

 

Sempre se pode usar outra linguagem, por exemplo uma que não tenha ponteiros. C não foi escrita para isso.

  • Obrigado 2
Link para o comentário
Compartilhar em outros sites

3 horas atrás, arfneto disse:

Cuidado: isso não vai impedir o acesso: é apenas uma convenção em seu programa passar o endereço de início e o de fim.

 

O acesso é desreferenciar seguindo de leitura|gravação, isto é, se não desreferenciar um ponteiro com endereço ainda não alocado, não acontece a falha de segmentação, evidente que nada impede o programador de passar um endereço desalocado e com isso causar falha por engano|intencional|experimental.

 

Uma estratégia é definir limite logicamente inválido:

Como é o caso das ‘strings’ com ‘byte’ 0, outro exemplo é uma lista de idades com idade negativo, até mesmo fornecer o capacidade|tamanho|quantidade das variáveis vizinhas (vetor). Nesse último caso, o primeiro elemento do vetor (primeira variável) tem o seu tamanho e a implementação (função que operará esse vetor) tem potencial para captar esse tamanho, ainda assim, nada impediria um argumento qualquer nessas funções de causar falha lógica ou intencional.

 

 

 

3 horas atrás, arfneto disse:

A Elite da Programação criou o protótipo de main() não por acaso como sendo

Não é O protótipo da main, mas sim UM protótipo da main, essa diferença é importante, apesar de parecer uma pouco pedante...

A elite de programadores em C chegou no consenso de que há situações em que esses argumentos são dispensáveis, por conta disso tanto a norma quando os compiladores modernos são inflexíveis quando à declaração int main(...).

 

Sendo main(int argc, char *arg[])

image.png.86f9bf20b0574ebca82c1b23cf559053.png

 

 

Sendo main(void)

image.png.d57ff06483e5569a10cab2317de778ff.png

 

Sendo main()

image.png.bf03883690c9890846c74ca7f15300bd.png

 

Sendo main(int argc, char **arg)

image.png.a35e260a9dc8d3c8fd38bea2dca1ff9f.png

Sendo que dois explicitamente descarta argumentos para aquelas situações em que são descartáveis.

 

 

 

Observe que na sua exemplificação de como causar uma falha modificou o índice de um ponteiro.

Nesse caso:

3 horas atrás, arfneto disse:
if (x < nums[100])

 

Veja seu análogo:

int exibir_numeros(int* ini, int* fim)
{
    if (ini == NULL) return -1;
    if (fim == NULL) return -1;
    printf("\nMostra valores de 0x%p a 0x%p (inclusive)\n", ini, fim[100]);
    if (ini > fim)
    {
        printf("\nx: 0x%p e maior que u_: 0x%p\n", ini, fim[100]);
        return -1;
    }
    int* p = ini;
    printf("Valores: [");
    for (int* p = ini; p <= fim; p += 1) printf(" %d", p[100]);
    printf(" ]\n");
    return 0;
}

Nada difere em nada 🙂. Com relação os argumentos da função pensei o mesmo que a Elite da Programação, porém com há discussão de duplos ponteiros continuei seguindo contagiado.

 

3 horas atrás, arfneto disse:

Mas não há qualquer garantia de que os endereços passados para exibir_numeros() sejam válidos.

Sempre se pode usar outra linguagem, por exemplo uma que não tenha ponteiros. C não foi escrita para isso.

🙂 Concordo não haver garantia.

 

11 horas atrás, mauro_b disse:

É impossível, pois o ponteiro (sendo uma variável) tem um número tal qual uma variável do tipo inteiro.

Link para o comentário
Compartilhar em outros sites

3 horas atrás, arfneto disse:
#include <stdio.h>
int exibir_numeros(int*, int*);
int main(void)
{
    int* de  = (int[5]){1, 2, 3, 4, 5};
    int* ate = de + 3;

    exibir_numeros(de, ate);
    exibir_numeros(ate, de);  // tenta ao contrario
    return 0;
}
int exibir_numeros(int* ini, int* fim)
{
    if (ini == NULL) return -1;
    if (fim == NULL) return -1;
    printf("\nMostra valores de 0x%p a 0x%p (inclusive)\n", ini, fim);
    if (ini > fim)
    {
        printf("\nx: 0x%p e maior que u_: 0x%p\n", ini, fim);
        return -1;
    }
    int* p = ini;
    printf("Valores: [");
    for (int* p = ini; p <= fim; p += 1) printf(" %d", *p);
    printf(" ]\n");
    return 0;
}

Capturar.PNG.bf33f816312e7283fac688f0a86b078d.PNG

 

🙂 — Dica final: ative os alerta do compilador,

lembre-se, ele foi/é desenvolvido com ajuda da Elite da Programação.

Link para o comentário
Compartilhar em outros sites

4 minutos atrás, mauro_b disse:

Dica final: ative os alerta do compilador,

 

Sr. @mauro_b eu pretendia mostrar os endereços de início e fim e eles estão lá na tela nos dois casos. 

 

Em geral uso o compilador MSVC e a opção /W3 e não há qualquer aviso em relação a esse código nesse nível. E se houvesse eu não daria a mínima porque sinceramente tudo que eu queria era mostrar os endereços... 

 

Usando sua experiência deve considerar que compiladores distintos tem comportamentos distintos --- implementation defined diz a norma em muitos aspectos --- e o gcc exigir void* para %p é uma chatice, um pedantismo conhecido há décadas imagino. 

 

Veja

 

Mostra valores de 0x0544FA5C a 0x0544FA68 (inclusive)
Valores: [ 1 2 3 4 ]

Mostra valores de 0x0544FA68 a 0x0544FA5C (inclusive)

x: 0x0544FA68 e maior que u_: 0x0544FA5C

 

Aí estão os resultados dos printf(). Os endereços. E era o esperado. 

 

1 hora atrás, mauro_b disse:

Observe que na sua exemplificação de como causar uma falha modificou o índice de um ponteiro.

 

É claro que  eu mudei. É o que estou mostrando... Como é frágil a sua convenção (ou qualquer outra) em relação a isso.

 

4 horas atrás, arfneto disse:

Mudando 1 linha em exibir_numeros() e passando a usar nums[100] como argumento no if

 

Imagino que tenha lido essa parte mas não citou em seu post... Escrevi para mostrar em seu programa que o vetor de dois ponteiros pode ser acessado na função bem além do tamanho sem qualquer queixa pelo compilador ou sistema.
 

E notou a referência ao tamanho reservado para o stack? Quanto maior a área mais longe pode ir na sua função exibir_numeros() antes de um erro de segmentação.

 

1 hora atrás, mauro_b disse:

A elite de programadores em C chegou no consenso de que há situações em que esses argumentos são dispensáveis, por conta disso tanto a norma quando os compiladores modernos são inflexíveis quando à declaração int main(...)

 

Pois é: são dispensáveis mas os compiladores são inflexíveis? 

 

image.png.713a205ab2bf622e376d1393e2026b0c.png

Não deu para entender o que tentou escrever. Há erros de concordância e não sei o que é "dois". Imagino que seja o segundo trecho.

 

Imagino que esteja tentando mostrar a tal inflexibilidade, mas lembro aos leitores que a inflexibilidade aqui é dada pela instrução -Werror passada na linha de comando.

 

O efeito disso é que o compilador trata QUALQUER aviso como um erro. Essa inflexibilidade nada tem a ver com a modernidade: é uma opção de compilação usada aqui para reforçar uma opinião --- a do sr. @mauro_b --- apenas.

 

Eis o trecho da norma onde trata disso, para que o curioso leitor não precise buscar 

 

5.1.2.2.1 [Program startup]
1   The function called at program startup is named main. The implementation declares no
    prototype for this function. It shall be defined with a return type of int and with no
    parameters:
            int main(void) { /* ... */ }
    or with two parameters (referred to here as argc and argv, though any names may be
    used, as they are local to the function in which they are declared):
            int main(int argc, char *argv[]) { /* ... */ }
    or equivalent;10) or in some other implementation-defined manner.
Footnote 10) Thus, int can be replaced by a typedef name defined as int, or the type of argv can be written as
        char ** argv, and so on.
2   If they are declared, the parameters to the main function shall obey the following
    constraints:
    -- The value of argc shall be nonnegative.
    -- argv[argc] shall be a null pointer.
    -- If the value of argc is greater than zero, the array members argv[0] through
       argv[argc-1] inclusive shall contain pointers to strings, which are given
       implementation-defined values by the host environment prior to program startup. The
       intent is to supply to the program information determined prior to program startup
       from elsewhere in the hosted environment. If the host environment is not capable of
       supplying strings with letters in both uppercase and lowercase, the implementation
       shall ensure that the strings are received in lowercase.
    -- If the value of argc is greater than zero, the string pointed to by argv[0]
       represents the program name; argv[0][0] shall be the null character if the
       program name is not available from the host environment. If the value of argc is
       greater than one, the strings pointed to by argv[1] through argv[argc-1]
       represent the program parameters.
    -- The parameters argc and argv and the strings pointed to by the argv array shall
       be modifiable by the program, and retain their last-stored values between program
       startup and program termination.

 

 

 

 

 

 

 

 

Link para o comentário
Compartilhar em outros sites

4 horas atrás, arfneto disse:
for (int* p = ini; p <= fim; p += 1) printf(" %d", *p);

 

Ps.: Apenas por curiosidade: o for difere tanto assim de WHILE.

Escolher entre um e outro é o quê?

int exibir_numeros(int* ini, int* fim)
{
    if (ini == NULL) return -1;
    if (fim == NULL) return -1;
    printf("\nMostra valores de 0x%p a 0x%p (inclusive)\n", ini, fim);
    if (ini > fim)
    {
        printf("\nx: 0x%p e maior que u_: 0x%p\n", ini, fim);
        return -1;
    }
    int* p = ini;
    printf("Valores: [");
    for (int* p = ini; p <= fim; p += 1) printf(" %d", *p);
    printf(" ]\n");
    return 0;
}
---------------------------------------------------------------------------------
.LC0:
        .string "\nMostra valores de 0x%p a 0x%p (inclusive)\n"
.LC1:
        .string "\nx: 0x%p e maior que u_: 0x%p\n"
.LC2:
        .string "Valores: ["
.LC3:
        .string " %d"
.LC4:
        .string " ]"
exibir_numeros:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 32
        mov     QWORD PTR [rbp-24], rdi
        mov     QWORD PTR [rbp-32], rsi
        cmp     QWORD PTR [rbp-24], 0
        jne     .L2
        mov     eax, -1
        jmp     .L3
.L2:
        cmp     QWORD PTR [rbp-32], 0
        jne     .L4
        mov     eax, -1
        jmp     .L3
.L4:
        mov     rdx, QWORD PTR [rbp-32]
        mov     rax, QWORD PTR [rbp-24]
        mov     rsi, rax
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        mov     rax, QWORD PTR [rbp-24]
        cmp     rax, QWORD PTR [rbp-32]
        jbe     .L5
        mov     rdx, QWORD PTR [rbp-32]
        mov     rax, QWORD PTR [rbp-24]
        mov     rsi, rax
        mov     edi, OFFSET FLAT:.LC1
        mov     eax, 0
        call    printf
        mov     eax, -1
        jmp     .L3
.L5:
        mov     rax, QWORD PTR [rbp-24]
        mov     QWORD PTR [rbp-16], rax
        mov     edi, OFFSET FLAT:.LC2
        mov     eax, 0
        call    printf
        mov     rax, QWORD PTR [rbp-24]
        mov     QWORD PTR [rbp-8], rax
        jmp     .L6
.L7:
        mov     rax, QWORD PTR [rbp-8]
        mov     eax, DWORD PTR [rax]
        mov     esi, eax
        mov     edi, OFFSET FLAT:.LC3
        mov     eax, 0
        call    printf
        add     QWORD PTR [rbp-8], 4
.L6:
        mov     rax, QWORD PTR [rbp-8]
        cmp     rax, QWORD PTR [rbp-32]
        jbe     .L7
        mov     edi, OFFSET FLAT:.LC4
        call    puts
        mov     eax, 0
.L3:
        leave
        ret


---------------------------------------------------------------------------------
---------------------------------------------------------------------------------

 

int exibir_numeros(int* ini, int* fim)
{
    if (ini == NULL) return -1;
    if (fim == NULL) return -1;
    printf("\nMostra valores de 0x%p a 0x%p (inclusive)\n", ini, fim);
    if (ini > fim)
    {
        printf("\nx: 0x%p e maior que u_: 0x%p\n", ini, fim);
        return -1;
    }
    printf("Valores: [");
    {
    int* p = ini; while (p <= fim){
        printf(" %d", *p);
        ++p;
        }
    }
    printf(" ]\n");
    return 0;
}
-----------------------------------------------------------------------------------
.LC0:
        .string "\nMostra valores de 0x%p a 0x%p (inclusive)\n"
.LC1:
        .string "\nx: 0x%p e maior que u_: 0x%p\n"
.LC2:
        .string "Valores: ["
.LC3:
        .string " %d"
.LC4:
        .string " ]"
exibir_numeros:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 32
        mov     QWORD PTR [rbp-24], rdi
        mov     QWORD PTR [rbp-32], rsi
        cmp     QWORD PTR [rbp-24], 0
        jne     .L2
        mov     eax, -1
        jmp     .L3
.L2:
        cmp     QWORD PTR [rbp-32], 0
        jne     .L4
        mov     eax, -1
        jmp     .L3
.L4:
        mov     rdx, QWORD PTR [rbp-32]
        mov     rax, QWORD PTR [rbp-24]
        mov     rsi, rax
        mov     edi, OFFSET FLAT:.LC0
        mov     eax, 0
        call    printf
        mov     rax, QWORD PTR [rbp-24]
        cmp     rax, QWORD PTR [rbp-32]
        jbe     .L5
        mov     rdx, QWORD PTR [rbp-32]
        mov     rax, QWORD PTR [rbp-24]
        mov     rsi, rax
        mov     edi, OFFSET FLAT:.LC1
        mov     eax, 0
        call    printf
        mov     eax, -1
        jmp     .L3
.L5:
        mov     edi, OFFSET FLAT:.LC2
        mov     eax, 0
        call    printf
        mov     rax, QWORD PTR [rbp-24]
        mov     QWORD PTR [rbp-8], rax
        jmp     .L6
.L7:
        mov     rax, QWORD PTR [rbp-8]
        mov     eax, DWORD PTR [rax]
        mov     esi, eax
        mov     edi, OFFSET FLAT:.LC3
        mov     eax, 0
        call    printf
        add     QWORD PTR [rbp-8], 4
.L6:
        mov     rax, QWORD PTR [rbp-8]
        cmp     rax, QWORD PTR [rbp-32]
        jbe     .L7
        mov     edi, OFFSET FLAT:.LC4
        call    puts
        mov     eax, 0
.L3:
        leave
        ret

🙂 

 

Link para o comentário
Compartilhar em outros sites

50 minutos atrás, mauro_b disse:

Ps.: Apenas por curiosidade: o for difere tanto assim de WHILE.

Escolher entre um e outro é o quê?

 

Não sei dizer. O "difere tanto" parece ser sua opinião, como escreveu.

 

No geral

  1. a escolha por um for é natural quando a lógica tem uma condição inicial definida, uma condição de fim e algum tipo de atualização a cada ciclo. 
  2. No caso de uma condição de fim ser o componente  mais determinado, como percorrer uma string usando while( *p++ != 0 ); p while é o preferido por muitos  e aparece até em códigos de conduta em um lugar ou outro.
  3. O terceiro loop , do {} while () aparece em casos em que claramente há ao menos 1 execução, e muitas vezes mesmo assim é preterido em relação a um while com o código inicial duplicado antes do loop.

 

No caso em que citou

 

for (int* p = ini; p <= fim; p += 1) printf(" %d", *p);

 

vs.

 

    int* p = ini; while (p <= fim){
        printf(" %d", *p);
        ++p;
        }

 

é um caso claro do item 1 acima. Ao ler o código de um for se sabe o que esperar em cada posição do comando. No caso do while é preciso ficar procurando, apesar de estarem os 3 comandos claramente na posição de inicialização, condição de saída e incremento.

Link para o comentário
Compartilhar em outros sites

1 hora atrás, arfneto disse:

Sr. @mauro_b eu pretendia mostrar os endereços de início e fim e eles estão lá na tela nos dois casos. 

😄 Senhor..., que isso..., haja cordialidade..., além disso, evidente que és mais velho, logo dirija-se a meu nick name da mesma maneira que faz com demais.

Com relação aos seus exemplos, agradeço muitíssimo, são tão caprichosos, nota-se o esforço para escrever sempre o melhor.

 

O que digo: parabéns!

 

 

1 hora atrás, arfneto disse:

Em geral uso o compilador MSVC e a opção /W3 e não há qualquer aviso em relação a esse código nesse nível. E se houvesse eu não daria a mínima porque sinceramente tudo que eu queria era mostrar os endereços... 

Essa é uma informação pertinente, obrigado por compartilhar que eu não devo usar MSVC, se bem que nunca usei mesmo, então é soma de zeros. Na próxima vez vou simplesmente ignorar. Seria interessante, talvez antes de postar seus resultados, informasse também no topo qual o modelo do compilador, considere isso!

 

 

1 hora atrás, arfneto disse:

É claro que  eu mudei. É o que estou mostrando... Como é frágil a sua convenção (ou qualquer outra) em relação a isso.

É claro que eu notei sua intenção, e fiz  igual mesmo... mostrei um exemplo análogo ao seu, no mínimo reforçando sua intenção, agora não resta dúvida quanto a fragilidade|usabilidade dos ponteiros. Ponteiros são velozes, mas sua usabilidade tem risco. Alguém ainda tem dúvida disso?

 

1 hora atrás, arfneto disse:

Imagino que tenha lido essa parte mas não citou em seu post... Escrevi para mostrar em seu programa que o vetor de dois ponteiros pode ser acessado na função bem além do tamanho sem qualquer queixa pelo compilador ou sistema.
 

E notou a referência ao tamanho reservado para o stack? Quanto maior a área mais longe pode ir na sua função exibir_numeros() antes de um erro de segmentação.

Sabemos que isso está relacionado ao grau de liberdade dos ponteiros não existe maneira mais frágeis ou menos frágeis. Só frágeis: usei um exemplo que utiliza pilha, a falha não é imediata talvez não acontecesse desde que ficasse na pilha, um espaço alocado. Penso que, foi isso.

 

Nota agora que apenas não considero interessante utilizar memória heap (alocação dinâmica de variáveis) para um experimento de risco, apesar de baixo.

Só isso!

 

 

1 hora atrás, arfneto disse:

magino que esteja tentando mostrar a tal inflexibilidade, mas lembro aos leitores que a inflexibilidade aqui é dada pela instrução -Werror passada na linha de comando.

 

O efeito disso é que o compilador trata QUALQUER aviso como um erro. Essa inflexibilidade nada tem a ver com a modernidade: é uma opção de compilação usada aqui para reforçar uma opinião --- a do sr. @mauro_b --- apenas.

MINHA OPINIÃO? Seja honesto... porque a opinião não é somente minha, mas também daqueles que implementam compiladores, o aviso não é exclusivo para alertar o desperdício de variável com assinaturas e argumentos da main, como está pretendendo educar com seu discursa dirigido à terceira pessoa, mas sim para desperdícios no programa.

 

Ainda que não fossem ativados, pense... qual o sentido de escrever o protótipo da main com argumentos que não pretende utilizar? Apenas para reforça um correto, assim a maioria dos códigos iniciantes, a proposito quase sempre desse fórum, escreveríamos exemplos com dois argumentos e não com nenhum, sendo nenhum a maioria.

 

Em resumo são 4 protótipos:

1. int main(int, int **);

2. int main(int, int *[]);

3. int main(void);

4. int main();

 

Quando não se dar utilidade para argumentos, recomenda (aviso) utilizar as assinaturas 3, 4

 

Aviso pega até os desperdícios menores:

2 horas atrás, mauro_b disse:
    int* p = ini;
    printf("Valores: [");
    for (int* p = ini; p <= fim; p += 1) printf(" %d", p[100]);
    printf(" ]\n");
    return 0;

image.png.8d6721fa23f73e6ba4ac4312ab35b8a0.png

Desperdício e desperdício não importa o tamanho.

 

5 horas atrás, arfneto disse:

Sugiro uma convenção mais segura, e usar um vetor de ponteiros null-terminated, como a "Elite da Programação"  como o Sr. @mauro_b chama, optou por usar em main().

Só observo seu conhecimento acerca da elite!

🙂 

 

23 minutos atrás, arfneto disse:

Não sei dizer. O "difere tanto" parece ser sua opinião, como escreveu.

Não entendi!

Link para o comentário
Compartilhar em outros sites

Esse exercício pede isso, eu fiz da seguinte forma:

 

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>

void exibeExpressao(int *ptra, int *ptrb, int **ptrp, int val)
{
    printf("\n\t---EXIBIR EXPRESSÕES---\n\n");

    printf("  A) ptrp[0][2]             :\t [%6X]\n", ptrp[0][2]); //APONTA PARA O ENDEREÇO DE PTRB E A "COLUNA" COMO NÃO FOI DECLARADA, TEM LIXO NA MEMÓRIA.

    printf("  B) ptra[4]                :\t [%6X]\n",ptra[4]); //TRATAR MEMORIA NÃO INSERIDA

    printf("  C) **ptrp + 3             :\t %i \n", **ptrp + 3); //CONTEUDO APONTADO POR PTRP = (CONTEUDO DO ENREÇO DE PTRB) VAI VALER 6

    printf("  D) *(ptrb + 1) + val      :\t %i \n", *(ptrb + 1) + val); //VAL = 5; PTBR[1] = 5; Conteúdo de PTRB[1] passa a valer 10.

    printf("  E) ptrp[0]                :\t %6X \n", ptrp[0]);//APONTA PARA O ENDEREÇO DO PTBR

    printf("  F) ++(*ptrb)              :\t %i \n", ++(*ptrb));//INCREMENTA 1 NO CONTEÚDO DE PTRB[0] == PTRA[1] que é 3, PASSA A VALER 4.

    printf("  G) *(ptrp - 1)            :\t %i \n", *(ptrp - 1)); //NÂO USAR INDICE NEGATIVO.

    printf("  H) *(ptrb + 1) * 2        :\t %i \n", *(ptrb + 1) * 2); //CONTEÚDO DE PTRB[1] que é 5, passa a valer 10.

    printf("  I) &(*ptrp)               :\t %6X \n", &(*ptrp)); //endereço DO PONTEIRO PTRP.

    printf("  J) *(&ptrb[1] - 2) + 3    :\t %i \n", *(&ptrb[1] - 2) + 3); //PTRB[-1] TEM UM VALOR 1 e +3, FICA 4- INDICE NEGATIVO.

    if(ptrb[1] == *(ptra+1))
    {
        printf("  K) ptrb[1] ==  *(ptra+1)  :    SÃO IGUAIS \n"); //PTRB[0] É IGUAL AO CONTEÚDO DE PTBRA[1]
    }else{
        printf("  K) ptrb[1] ==  *(ptra+1)  :    NÃO SÃO IGUAIS \n");
    }

    printf("  L) *(ptrp-(*ptrb-6)       :\t %i \n", *(ptrp-(*ptrb-6)));//ACESSA MEMÓRIA NÂO ALOCADA

    printf("  M) val**ptrb              :\t %i \n", val**ptrb);//VAL = 5 & PTRB = 4

    printf("  N) ptrb + ptra[1] - 5     :\t %6X \n", ptrb + ptra[1] - 5); //ENDEREÇO

    printf("  O) **(&ptrb+1)            :\t %6X \n\n", **(&ptrb+1));

    system("\tpause");

}

int main()
{
    setlocale(LC_ALL, "Portuguese");

    int i;
    int *ptr_b;
    int *ptr_a;
    int **ptr_p;
    int val = 55;

    ptr_a = (int*)malloc(4*sizeof(int));
    ptr_b = &ptr_a[1];
    ptr_p = &ptr_b;

    printf("\n\t--- VALORES INCIAIS ---\n\n");

    for(i=0; i<4;i++)
    {
        ptr_a[i] = 2*i+1;
        printf("  ptr_a [%i]:\t%d\n",i,ptr_a[i]);
    }

    val = ptr_b[1];

    printf("\n  ptr_a:\t%6X\n", ptr_a);
    printf("  ptr_b:\t%6X\n", ptr_b);
    printf("  ptr_p:\t%6X\n\n", ptr_p);


    printf("---------------------------------------\n\n");
    system("pause");

    exibeExpressao(ptr_a,ptr_b,ptr_p,val);

    free(ptr_a); 


    return 0;
}

 Só que eu não entendi a parte para de imprimir  a palavra erro. Por isso eu queria fazer algo  daquele jeito quer eu disse. Porém eu consigo saber de cabeça onde cada  um vai acessar uma memória não alocada. Mas Essa parte de imprimir erro na região de memória não permitida que eu estou confuso. Existe região de memória não permitida ? Como saber se tal variável está tentando acessá-la?

 

Link para o comentário
Compartilhar em outros sites

3 minutos atrás, Lipeco disse:

Só que eu não entendi a parte para de imprimir  a palavra erro. Por isso eu queria fazer algo  daquele jeito quer eu disse. Porém eu consigo saber de cabeça onde cada  um vai acessar uma memória não alocada. Mas Essa parte de imprimir erro na região de memória não permitida que eu estou confuso. Existe região de memória não permitida ? Como saber se tal variável está tentando acessá-la?

 

Poste a parte inicial do enunciado. Não está claro onde as declarações de seu programa fazem parte do enunciado e assim fica difícil de entender. Existe sim região de memória não permitida, na verdade o contrário. Há muitos anos existem controladores de acesso a memória, em hardware.

 

Seu programa tem um espaço de endereçamento próprio, um tamanho de pilha próprio como discuto aqui pra cima. Se você tentar acessar algo fora disso deve ter um erro e cancelar seu programa. Em especial se tentar gravar. Mas de dentro do programa pode não ser possível determinar isso.

 

Pode ser que seu exercício não espere que seu programa identifique isso sozinho, mas sim você faça isso e produza as mensagens de acordo.

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

@arfneto Amigo esse é o início do exercício, não pede mais nada, passa apenas o início do programa, almoçando ptra, e passando as variáveis semente, tem a segunda parte, mas a segunda parte pede outra coisa, apenas pra fazer expressões equivalentes a essa com o mesmo resultado. 

Acho que é o que você disse, que eu faça e coloque a mensagem de acordo. 

Link para o comentário
Compartilhar em outros sites

19 minutos atrás, mauro_b disse:

MINHA OPINIÃO? Seja honesto... porque a opinião não é somente minha, mas também daqueles que implementam compiladores, o aviso não é exclusivo para alerta o desperdício de memória com assinaturas e argumentos da main como está pretendendo educar com seu discursa dirigido à terceira pessoa, mas sim para desperdícios no programa

 

Isso está ficando bem fora do tópico. Mas de todo modo entenda que isso não se aplica a main(), não há qualquer desperdício de memória e se tivesse não seria com assinaturas exceto pelo tempo de compilação.

 

Entenda: ao chamar o programa o sistema operacional monta o bloco de parâmetros. Mesmo que o programa tenha especificado int main(void) ou int main() se o programa for executado com 12 parâmetros o sistema vai montar o bloco todo, com 12+2, e empilhar o endereço de retorno, carregar o programa na memória e iniciar a execução em main().

 

26 minutos atrás, mauro_b disse:

Senhor..., que isso..., haja cordialidade..., além disso, evidente que és mais velho, logo dirija-se a meu nick name da mesma maneira que faz com demais

 

Não entendi a evidência de que seja eu mais velho 🙂 

image.png.29df740f8585d2c462d890e92df897c2.png


Você perguntou no que eles diferem tanto, os comando for e while. Foi isso. Foi sua expressão. Eu só respondi e listei as considerações mais comuns ao se fazer essa opção.

 

image.png.0eb28defb3ce6cacc15f241159929b35.png

 

A norma estabelece que o bloco de parâmetros seja seguido por um NULL. Foi isso que eu quis dizer e mostrei até o texto da normal. Apenas não traduzi.

2 minutos atrás, Lipeco disse:

@arfneto Amigo esse é o início do exercício, não pede mais nada, passa apenas o início do programa, almoçando ptra, e passando as variáveis semente, tem a segunda parte, mas a segunda parte pede outra coisa, apenas pra fazer expressões equivalentes a essa com o mesmo resultado. 

Acho que é o que você disse, que eu faça e coloque a mensagem de acordo. 

 

Mas esses itens citam variáveis, e se o enunciado não diz exatamente como elas são declaradas vai ser um bagunça, já que cada um vai ter que criar as suas . E se o enunciado diz é exatamente o que te perguntei...

 

image.png.a7888d44071b4028c69adb9513607648.png

Cada um vai poder declarar essas coisas coo preferir?

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

@arfneto  Entendi amigo.
Passa esse como inicio


    int i;
    int *ptr_b;
    int *ptr_a;
    int **ptr_p;
    int val = 55;

    ptr_a = (int*)malloc(4*sizeof(int));
    ptr_b = &ptr_a[1];
    ptr_p = &ptr_b;

    printf("\n\t--- VALORES INCIAIS ---\n\n");

    for(i=0; i<4;i++)
    {
        ptr_a[i] = 2*i+1;
        printf("  ptr_a [%i]:\t%d\n",i,ptr_a[i]);
    }

    val = ptr_b[1];

    printf("\n  ptr_a:\t%6X\n", ptr_a);
    printf("  ptr_b:\t%6X\n", ptr_b);
    printf("  ptr_p:\t%6X\n\n", ptr_p);

 

A partir daí fazer as expressões. Fiz um desenho pra não me perder.

 

trab2.jpg

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...