förmodligen en simpel C fråga

C, C++, Pascal, Assembly, Raspberry, Java, Matlab, Python, BASIC, SQL, PHP, etc.
DennisCA
Inlägg: 2249
Blev medlem: 13 maj 2014, 08:44:18
Ort: Finland, österbotten

förmodligen en simpel C fråga

Inlägg av DennisCA »

Jag håller på och läser igenom C boken "C Programming Language: 2nd Edition" och är på kapitlet om arrayer så länge. Det första jag stötte på här var att i originalkoden i boken så skulle man skriva en funktion som heter getline, men det resulterade i fel. Det visade sig att funktionen getline har lagts till i C språket sedan boken skrevs. Så jag gjorde den att heta getline2 helt enkelt för att komma vidare. Intressant vilka saker man stöter på med en gamal bok, lärde mig nåt extra på det viset tycker jag.

Jag har nu skrivit av programmet ur boken och tror att det funkar som författarna menar, det går igenom raderna i t.ex. en textfil (./program < textfil) och när den största raden påträffas så skrivs den längsta linjen ut hädanefter istället för de rader som verkligen kommer. Lite pointless men ska väl visa konceptet.

Men jag har problem att konceptuellt fatta hur vissa delar av programmet funkar. Har inte fått den där Aha upplevelsen ännu... Det känns som koden bryter mot hur jag förstår funktioner. T.ex. i den första while loopen i main() med funktionen getline2.

Som jag tolkar koden så ser det ut såhär:
  1. Man skickar följande argument: getline2(1000, 1000).
  2. Väl inne i funktionen används funktionen getchar() för att få extern data som t.ex. från en textfil
  3. Det loopar genom tills raden stöter på '\n' och räknar hur lång raden är samt lagrar varje karatär i array s[], avslutar alltid med '\0' vilket är rätt.
  4. Funktionen returnerar radens längd.
  5. Om radens längd > max så definiera radens längd som nya maxvärdet.
  6. kör copy funktionen som kopierar värdet line > longest
  7. skriv ut longest.
Det som jag inte förstår här är hur kan man ens kalla på funktionen copy med värdet line? Varifrån kommer line? Var definierades line som nåt annat än 1000? Det ser ut som att line blev = s[] inne i getline2. Men det är det jag inte förstår... Funktionen getline2 så returernar bara en int?

Som jag har förstått funktioner så array:n s[] som finns innuti getline2 kommer inte ur ur funktionen och är en temporär variabel som försvinner om den inte returernas? Men här verkar det som s[] slapp ut ur funktionen, blev definierad som line och kunde nyttjas av copy.

Jag har lite samma grunningar gällande copy funktionen. Jag förstår den kopierar from > på ett ganska elegant sätt med en while loop. Men förstår inte hur den informationen slipper ut ur copy då den är void. Men uppenbarligen gör den det. Det verkar inte som att en funktion uttryckligen måste returnera data. Det ser ut som att informationen returernas via parametrarna som används för att anropa funktionerna och det är så datan för line slipper ut ur getline2 samt ur funktionen copy. Eller har jag fattat det fel?

Ganska mycket svammel här hoppas det går att förstå. Men jag vill inte gå vidare innan jag fått den här Aha upplevelsen och koden plötsligt klickar för mig.

Kod: Markera allt

#include <stdio.h>
#define MAXLINE 1000

/* declare function prototypes */
int getline2(char line[], int maxline);
void copy(char to[], char from[]);

/* print longest input line */
int main()
{
  int len; /* current line length  */
  int max; /* maximum length seen so far  */
  char line[MAXLINE]; /* current input line  */
  char longest[MAXLINE]; /*  longest line saved here  */

  max = 0;
  while ((len = getline2(line, MAXLINE)) > 0)
  {
    if (len > max) {
      max = len;
      copy(longest, line);
    }

    if (max > 0) // there was a line
    {
      printf("%s", longest);
    }
  }
  return 0;
}
/* getline: read a line into s, return length */
int getline2(char s[], int limit)
{
  int c, i;

  for (i = 0; i < limit-1 && (c = getchar()) != EOF && c !='\n'; i++) {
    s[i] = c;
  }
  if (c == '\n') {
    s[i] = c;
    ++i;
  }
  s[i] = '\0';
  return i;
}

/* copy: copy 'from' into 'to'; assume its big enough */
void copy(char to[], char from[])
{
  int i;
  i = 0;

  while ((to[i] = from[i]) != '\0') {
    ++i;
  }
}
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 45175
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: förmodligen en simpel C fråga

Inlägg av TomasL »

Du har ju definierat "line" och "longest" som char-array's
Copy skickar pekare till första elementet av "longest" och "line".
En array är per definition en pekare, även om du inte definerar den som pekare.

Hittade detta som kanske förklarar lite mer
https://stackoverflow.com/questions/164 ... y%20itself.
So, in summary, array names in a C program are (in most cases) converted to pointers. One exception is when we use the sizeof operator on an array. If a was converted to a pointer in this context, sizeof a would give the size of a pointer and not of the actual array, which would be rather useless, so in that case a means the array itself.
Senast redigerad av TomasL 8 januari 2023, 11:08:17, redigerad totalt 1 gång.
davidi
Inlägg: 571
Blev medlem: 13 oktober 2011, 16:45:38
Ort: Ekerö

Re: förmodligen en simpel C fråga

Inlägg av davidi »

Du kallar inte på funktionen copy() med värdet line, utan du kallar på funktionen copy() med pekare till de båda arrayerna som argument. Copy får minnesadresserna till startpositionerna i de båda arrayerna, och även om funktionen inte returnerar något så har den förändrat innehållet i to-arrayen.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 45175
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: förmodligen en simpel C fråga

Inlägg av TomasL »

Du vet at det finns en "övningsbok" till huvudboken.
"The C answers book"
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
DennisCA
Inlägg: 2249
Blev medlem: 13 maj 2014, 08:44:18
Ort: Finland, österbotten

Re: förmodligen en simpel C fråga

Inlägg av DennisCA »

Just det ja, pekare. Det är ett koncept jag är ovan med ännu.
Användarvisningsbild
rvl
Inlägg: 5721
Blev medlem: 5 april 2016, 14:58:53
Ort: Helsingfors

Re: förmodligen en simpel C fråga

Inlägg av rvl »

Saker fastnar antagligen bättre när man skriver sjäv, men du kan också samgoogla boknamnet med github, om du vill vara litet lat.
DennisCA
Inlägg: 2249
Blev medlem: 13 maj 2014, 08:44:18
Ort: Finland, österbotten

Re: förmodligen en simpel C fråga

Inlägg av DennisCA »

Jag försöker att skriva ner all kod manuellt som förekommer i boken, jag har inte facitboken men hittade denna sida, jag går alltid dit i efterhand och jämför resultaten av mina egna övningar mot vad som står där.

https://www.learntosolveit.com/cprogramming/index.html
Användarvisningsbild
4kTRB
Inlägg: 18290
Blev medlem: 16 augusti 2009, 19:04:48

Re: förmodligen en simpel C fråga

Inlägg av 4kTRB »

Om du hade programmerat i assembler innan du börjar med C hade du fått en mycket klarare bild av det hela.

char to[], char from[] är argument till proceduren

char from[] är till för att läsas och kallas "called by value"
adressen till arrayen from[] ges till proceduren (line) i det här fallet, då line är en array, ett block med data som börjar på en viss adress.

char to[] ska skrivas om och kallas "called by name"
och man måste veta var den variabeln finns i programmet
så här ges dess adress till proceduren (longest), också ett block av viss längd som reserverats i minnet.

Parameterblocksöverföring kallas det när man programmerar i assembler
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 45175
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Re: förmodligen en simpel C fråga

Inlägg av TomasL »

Tycker du rör ihop det onödigt nu.
Normalt i C så gäller "called by value", dvs det aktuella värdet skickas till funktionen och i regel har man då ett returvärde från funktionen.
Man kan skicka variabelreferenser i stället, och då skickas adressen till variabeln i stället för värdet, funktionen kan då påverka variablerna och man behöver oftast inger returvärde.

Arrayer är lite speciella, då de i regel omvandlas till pekare vid funktionsanrop, dvs funktionen får adressen till arrayen i stället för dess värde, och det är just det som händer i exempelkoden.
Normalt sett om man skall skicka en referens till en variabel, måste man skapa en pekare, och ge pekaren värdet av variabelns adress, men detta sker per automatik med en array.
Följande är identiska

Kod: Markera allt

char *p;
char arr[10];
p=arr
// man kan också skriva
p = &arr[0];
// Följande är identiska
p ==  arr[0];
p+1 == arr[1];
pekare är nog det mest missförstådda i C, men egentligen är det rätt enkelt och synnerligen användbart
Användarvisningsbild
4kTRB
Inlägg: 18290
Blev medlem: 16 augusti 2009, 19:04:48

Re: förmodligen en simpel C fråga

Inlägg av 4kTRB »

Called by name finns väl inte i C då procedurerna inte kan ändra argumenten som används vid anropet.
Å det gör det inte i copy-proceduren heller då det är adresser som skickas med.

call by name hade fungerat ungefär så här

int A =2;
square(A);
print(A) // hade blivit 4 i utskriften
==============================================
call by value

int A =2;
square(A);
print(A); // hade blivit 2 i utskriften

och

int A =2;
A = square(A);
print(A); // hade blivit 4 i utskriften

==============================================

Även om det i högnivå inte är call by name så kan kan mycket väl en kompilerad procedur använda call by name.
Proceduren måste veta var i minnet variabeln den ska modifiera finns medans om proceduren bara ska läsa en parameter
så är den call by value.
Användarvisningsbild
4kTRB
Inlägg: 18290
Blev medlem: 16 augusti 2009, 19:04:48

Re: förmodligen en simpel C fråga

Inlägg av 4kTRB »

Lite tydligare eventuellt...
En assemblersubrutin översatt till högnivåspråk...

square(TAL,KVADRAT)

procedure square( int TAL, int KVADRAT)
int TEMP;
begin
TEMP:=TAL;
KVADRAT:= TEMPxTEMP;
end


TAL är call by value och KVADRAT är call by name
eftersom KVADRAT är deklarerad i minnet och ska ändras
TAL behöver bara läsas och har levererats som en kopia till proceduren

//***********************************************
//* Placera TAL i D0
//* Kvadraten av TAL returneras i D0
//* Anrop: jsr square
//* *********************************************
square:
move.w d0,d1
muls d1,d1
move.l d1,d0
rts


Anrop: (allt innanför strecken motsvarar en funktion square(argument) i högnivåspråk)
----------------------------------------------------------------------------------------------------------
move.w argument,d0 //hämta talet som ska kvadreras, kopiera till d0
jsr square
move.l d0,KVADRAT //lagra resultatet, skriv över det gamla värdet där KVADRAT deklarerats i minnet
----------------------------------------------------------------------------------------------------------

Alltså TAL (argumentet) har endast blivit läst i subrutinen
medans KVADRAT har blivit ändrad, KVADRAT är en deklarerad minnesposition om 32 bitar (long)

Så kan man lösa en funktion i C, som ovan, och då blir det call by value (i C) då B inte ändras

int A = 0;
int B = 2;

A := square(B);

och A blir 4 och B är fortfarande 2

eller

A := square(A);

där A inte ändras förrens funktionen är klar.
Användarvisningsbild
pi314
Inlägg: 5680
Blev medlem: 23 oktober 2021, 19:22:37
Ort: Stockholm

Re: förmodligen en simpel C fråga

Inlägg av pi314 »

Jag tycker det här är en ganska överskådlig beskrivning av hur det fungerar i C.
Call by value and Call by reference in C
https://www.javatpoint.com/call-by-valu ... rence-in-c

/Pi
Mr Andersson
Inlägg: 1394
Blev medlem: 29 januari 2011, 21:06:30
Ort: Lapplandet

Re: förmodligen en simpel C fråga

Inlägg av Mr Andersson »

Den där sidan har fel. Det står klart och tydligt i standarden att C endast har call by value.
Det som vissa tror är by reference i C är att skicka en pekare by value och sen dereferera den.
Användarvisningsbild
Marta
EF Sponsor
Inlägg: 6889
Blev medlem: 30 mars 2005, 01:19:59
Ort: Landskrona
Kontakt:

Re: förmodligen en simpel C fråga

Inlägg av Marta »

När jag skulle kravla upp ur Pascalträsket var den här till stor hjälp. https://en.wikibooks.org/wiki/A_Little_ ... nt_version

Det Du har hittat nu tycker jag verkar rörigt och lär ut obra saker. Man använder inte open array som argument i funktioner. char *varname är det rätta. För kommentarer används //en kommentar. Det är enradare som slutar vid radrytningen. Snabbare, enklare och Du har /* */ kvar att användas vid felsökning för att tillfälligt ta bort ett avsnitt.
Användarvisningsbild
pi314
Inlägg: 5680
Blev medlem: 23 oktober 2021, 19:22:37
Ort: Stockholm

Re: förmodligen en simpel C fråga

Inlägg av pi314 »

Mr Andersson skrev: 9 januari 2023, 01:49:44 Den där sidan har fel. Det står klart och tydligt i standarden att C endast har call by value.
Det som vissa tror är by reference i C är att skicka en pekare by value och sen dereferera den.
"Simulated call by reference" kallas det ibland när man skickar en pekare till en funktion i C och sedan t.ex. ändrar originalvariabeln genom att referera till den via pekaren.

Men, du har nog rätt att det är slarvigt eller oegentligt att kalla det call by reference.

Men, är språkbruket 100% entydigt? Kanske är det slarvet med språket som är utbrett?

/Pi
Skriv svar