AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79560677
Accepted
unnecessary_bootstrapping
unnecessary_bootstrapping
Asked: 2025-04-08 03:54:12 +0800 CST2025-04-08 03:54:12 +0800 CST 2025-04-08 03:54:12 +0800 CST

É aceitável passar um ponteiro como argumento para um ponteiro duplo em C?

  • 772

Então, tenho feito alguns projetos de aprendizagem em C e estou começando a ficar mais confortável com ponteiros. No entanto, me deparei com alguns fenômenos sobre os quais não consigo encontrar muitas informações.

Basicamente, eu crio um array, para simplificar, estou começando com um array de inteiros:

int x[2] = {1, 3};

Agora, se eu quiser modificar o conteúdo deste array, descobri que posso criar uma função que recebe um ponteiro inteiro como parâmetro, passa xcomo argumento e desreferencia xespecificando um índice.

#include <stdio.h>

void foo(int* input){
    input[0] = 2;
    input[1] = 4;
}

int main(){
    int x[2] = {1, 3};
    printf("x[0] before: %d\nx[1] before: %d\n", x[0], x[1]);
    foo(x);
    printf("x[0] after: %d\nx[1] after: %d\n", x[0], x[1]);
}

saída

x[0] before: 1
x[1] before: 3
x[0] after: 2
x[1] after: 4

o que é interessante, mas não vejo isso sendo feito com frequência, então não tenho certeza se é aceitável.

Agora, para a questão maior, por algum motivo, sempre que quero alterar o endereço para o qual o próprio ponteiro aponta, especificando um novo valor e ponteiro e, em seguida, definindo o argumento para esse novo ponteiro, isso só parece funcionar quando eu faço o seguinte:

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

void foo(int** input){
    int x[3] = {2, 4};
    int** ptr = x;
    *input = *ptr;
}

int main(){

    int x[2];
    x[0] = 1;
    x[1] = 3;
    printf("x[0] before: %d\n", x[0]);
    printf("x[1] before: %d\n", x[1]);
    foo(x);
    printf("x[0] after: %d\n", x[0]);
    printf("x[1] after: %d\n", x[1]);

    return 0;
}

Saída:

x[0] before: 1
x[1] before: 3
x[0] after: 2
x[1] after: 4

Então, tenho várias perguntas para as quais não consigo encontrar uma resposta, provavelmente devido à minha falta de vocabulário técnico:

1: Sempre que passo o array com e sem referência como argumento, por que o argumento se comporta como um array de ponteiros, sem inicializar um array de ponteiros e defini-lo primeiro para o endereço do array? Há alguma consequência não intencional em fazer isso?

2: Se o argumento for um ponteiro, em vez de um ponteiro para um ponteiro, é aceitável passar um ponteiro como argumento para uma função que recebe um ponteiro duplo como parâmetro, como eu fiz? Existe algum comportamento indefinido ao fazer isso que eu deva saber?

3: Por que isso funciona?

arrays
  • 2 2 respostas
  • 101 Views

2 respostas

  • Voted
  1. Eric Postpischil
    2025-04-08T04:10:43+08:002025-04-08T04:10:43+08:00

    ... o que é interessante, mas não vejo isso sendo feito com frequência, então não tenho certeza se é aceitável.

    É normal e comum fornecer um array como argumento para um parâmetro que é um ponteiro. O array é automaticamente convertido em um ponteiro para seu primeiro elemento.

    Essa conversão é realizada sempre que uma matriz é usada em uma expressão diferente de como operando de sizeof, como operando de unário &, como operando de um operador typeof ou como um literal de string usado para inicializar uma matriz.

    Sobre este código:

    void foo(int** input){
        int x[3] = {2, 4};
        int** ptr = x;
        *input = *ptr;
    }
    

    Isso é horrível e você nunca deveria fazer isso.

    O que realmente está acontecendo aqui é:

    • Você forneceu o array xcomo argumento. Isso foi automaticamente convertido em um ponteiro para o seu primeiro elemento. Ou seja, um int *cujo valor é &x[0].
    • fooé declarado para receber um int **, então o compilador converteu o ponteiro para o tipo int **. Mas ele ainda aponta para x[0].
    • int** ptr = x;inicializa ptrpara apontar para o primeiro elemento do xlocal to foo. Novamente, o compilador converte o ponteiro para o tipo int **.
    • Em *input = *ptr;, ptré desreferenciado. Como seu tipo é int **, o compilador espera que ele se refira a um int *. Na sua implementação em C, um int *ocupa oito bytes, então o compilador carrega oito bytes da memória.
    • Na sua implementação em C, intsão quatro bytes, então oito bytes são dois int. Quando o compilador carrega oito bytes da memória, ele obtém os dois intpara os quais o ponteiro está apontando, portanto, ele obtém os bytes de x[0]e x[1](no array local x).
    • Então, para a atribuição a *input, o compilador espera *inputque se refira a oito bytes e grava os oito bytes carregados na memória. Esta é a memória de x[0]e x[1]em main, então esses elementos são substituídos pelos bytes de xem foo.

    Tudo isso "funcionou" por coincidência. Na verdade, trata-se de um comportamento que não é definido pelo padrão C: essas conversões implícitas de ponteiros não são definidas, e o acesso à memória usando o tipo errado não é definido. Se você habilitar a otimização no compilador, o programa pode parar de "funcionar".

    • 3
  2. Best Answer
    Andreas Wenzel
    2025-04-08T04:10:09+08:002025-04-08T04:10:09+08:00

    Não há nada de errado com o seu primeiro programa. No entanto, o segundo programa está invocando um comportamento indefinido devido à violação da regra de aliasing estrita , pois está acessando um intcomo se fosse um int *.

    O que está acontecendo no seu segundo programa é o seguinte:

    A linha

    int** ptr = x;
    

    não é permitido no C padrão, pois converte a int *para an int **sem uma conversão de tipo explícita. O compilador Clang apenas emite um aviso, enquanto o compilador GCC rejeitará esta linha imediatamente. Portanto, presumo que você esteja usando o compilador Clang.

    O compilador Clang tratará esta linha como se você tivesse usado uma conversão de tipo explícita:

    int** ptr = (int **)x;
    

    Na próxima linha

    *input = *ptr;
    

    Você está tratando o ponteiro ptrcomo se estivesse apontando para um objeto do tipo int *, embora ele esteja apontando para um simples int. Como mencionado anteriormente, isso viola a regra estrita de aliasing (invocando, assim, um comportamento indefinido), mas, no seu caso, parece que você teve sorte e que funciona como esperado. Isso provavelmente se deve ao fato de você estar em uma plataforma na qual os ponteiros têm 64 bits de tamanho, que por acaso é o mesmo tamanho de todo o array.

    Para a CPU, copiar um ponteiro de 64 bits é o mesmo que copiar um array de dois valores de 32 bits, porque o tamanho total dos dados é de 64 bits em ambos os casos. No entanto, da perspectiva do compilador, não é a mesma coisa, pois você está quebrando as regras da linguagem C, e tais coisas podem facilmente dar errado se o compilador decidir reorganizar seu código para fins de otimização.

    Se você quiser que a função fooaltere a variável xna função mainpara que ela se refira a um array diferente, você deve tornar a variável xum ponteiro em vez de um array, para que ela tenha a capacidade de apontar para um array diferente. Se você então passar um ponteiro para o ponteiro xda função foo, a função foopoderá usar esse ponteiro para alterar o valor do ponteiro x, de modo que ele aponte para um array diferente. Aqui está um exemplo:

    #include <stdio.h>
    
    void foo( int **input )
    {
        // This array must have static storage duration, otherwise
        // its lifetime will end as soon as the function terminates.
        static int second_array[2] = { 2, 4 };
    
        *input = second_array;
    }
    
    int main( void )
    {
        int first_array[2] = { 1, 3 };
        int *x = first_array;
        
        printf("x[0] before: %d\nx[1] before: %d\n", x[0], x[1]);
        foo( &x );
        printf("x[0] after: %d\nx[1] after: %d\n", x[0], x[1]);
    }
    

    Este programa tem a seguinte saída:

    x[0] before: 1
    x[1] before: 3
    x[0] after: 2
    x[1] after: 4
    
    • 2

relate perguntas

  • Possível inicializar um ponteiro de um valor na matriz de ponteiros?

  • Possível inicializar o ponteiro para a matriz de variáveis ​​digitadas?

  • Swift Array, como recuperar todos os elementos em uma enumeração aninhada de uma matriz

  • Por que uma string C nem sempre é equivalente a uma matriz de caracteres?

  • PowerShell: Como transformar valores de matriz hastable como este?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve