Denna funktion genererar ett segmentation error. Den ska bara ta en sträng, stympa den och skriva ut bitarna med andra strängar emellan bitarna. Kanske lite dåligt förklarat men koden är rätt så simpel. Jag har fått liknande kod att fungera, säg till om jag ska posta den. Jag sitter på en Debian Sarge (Linux) och kompilerar med GCC men jag tror inte att Windows skiljer sig från Linux i detta fallet. Hmm... den där koden ser inget vidare ut. Alla funktionerna (strncopy, strstr) som jag använder finns ju i string. Så vitt jag förstår så skriver strncopy bara över "dd" med "\0\0", så ingen mera plats behövs. Vad är det för sträng du skickar in? Programmet kompilerar... :( Hej! <b>>Alla funktionerna (strncopy, strstr) som jag använder finns ju i string.</b> <b>Måhända är jag helt snurrig nu, men vad jag vet så skall alla moderna kompilatorer ha sina c-strängfunktioner i <cstring> och alla sina vanliga i <string>. Den gamla <string.h> är jag osäker på vilken det motsvarar.</b> Jag ändrade på koden och lade in: Läs Martin Adrians inlägg ovan: Hej! <b>Felet uppstår när du i anropet till strncpy anger att du ska kopiera 2 tecken.</b> Jaaa du... strncpy kopierar bara fram till första '\0' dock max "n" tecken. Eftersom strängen som skall kopieras ("\0\0") är tom (första tecknet är '\0') så kopierar strncpy bara ett tecken. Hävdar fortfarande att krashen beror på att kompilatorn placerat strängen i ett read-only segment. Förstår dock inte varför kompilatorn inte ger en varning vid anropet av print (conversion from "const char[]" to "char*") <b>Det beror på strncpy, den försöker kopiera det antal bytes som angivits, som inte "finns".</b> Hej! <b>>Vad då 'inte "finns"'? Nolltecknena finns ju.</b> <b>>Vet inte riktigt vad ett read-only segment (processorn?) skulle vara i det här fallet, </b> <b>>Alltså, om du har en konstant sträng, typ const char[]="arne", så är den readonly (i princip)</b> tillbaka till frågan. Vi kan för tillfället säga så här: alla konstanter är lika konstanta. Alltså säger vi att alla const ... eller alla literaler, etc. ligger på ett låst område av minnet som man inte har tillgång till. Jag får tacka så mycket, med: >Vet inte riktigt vad ett read-only segment (processorn?) skulle vara i det här fallet, <b>Ni har väl hört reklamen om AMD64:s inbyggda virusskydd. Nu vet ni vad det är också.</b> Hej igen! Man kan även ha pekarvariabler som inte får ändras, men det som de pekar på får ändras: Jävlars, nu tänkte jag fel ändå!Skriva på sträng
#include <string>
#include <iostream>
void print(char string[], char *strings[], int argc){
int i;
char * pointer_1, * pointer_2;
pointer_1=&string[0];
while(i<argc){
pointer_2 = strstr (pointer_1,"dd"); //Hittar "dd" i strängen och sätter en pekare att peka dit
if(pointer_2==NULL){
cout<<"null!!"<<endl;
break;
}
strncpy(pointer_2,"\0\0",2); // <------ Denna rad generar ett segmentation error.
cout<<pointer_1<<endl<<endl; //Skriver ut
cout<<strings[i]<<endl;
pointer_1=pointer_2+2; //Sätt pekare "pointer_1" att peka på nästa bit av strängen
i++;
}
}
Sv: Skriva på sträng
Grejen är det här:
1. Du inkluderar fel header.
#include <string>
innebär att du inkluderar string. string är C++-biblioteket för klassen string. Det du använder i koden är dock c-strängar, så om något bör du inkludera <cstring>. Till detta kommer namespace också.
2. Du kör med C-strängar i onödan. Använd hellre <string> tillsammans med C++-strängarna "string" och C++-arrayerna "vector". Slipper du bry dig om minneshantering etc.
3. Du kallar strängarna för string, det är dumt när det redan finns klasser som heter det, etc.
Nåväl, om du inte väljer att gå över till det väsentligt mycket bättre standardbiblioteket (snabbare, snyggare, enklare, mindre resurskrävande) så ser jag nog bara att du använder "\0\0", vilket ju i praktiken blir tre stycken \0.
Använd hellre *pointer_2=0; *(pointer_2+1)=0; eller något liknande i så fall.Sv:Skriva på sträng
Jag VILL lära mig c-strängar! :P
strcopy kopierar bara över så många tecken som jag anger, i detta exemplet 2. Det lägger inte till ett sista '\0' efter tecknen.
*pointer_2=0; *(pointer_2+1)=0; fungerade inte, får ändå segmentation error
Den här koden fungerar utan problem:
int main ()
{
char str[] ="hej dd da";
char * pointer;
pointer = strstr (str,"dd");
strncpy (pointer,"\0\0",2);
cout<<str<<endl;
cout<<(pointer+2)<<endl;
return 0;
}
och den fungerar ju nästan likadant, eller?
Sv:Skriva på sträng
Segmentation error är väl när man går utanför sina egna data och börjar skriva (eller läsa?) på platser som man inte allokerat? Eller?Sv: Skriva på sträng
Om det är en konstant sträng t.ex. "abcdd" så kan du få ett segmentation fault om compilatorn placerat strängen i read-only-memory.
Skillnaden mot det andra exemplet är att där kan inte kompilatorn placera strängen i read-only-memory eftersom den tilldelas till en icke-const variabel.
Jämför följande:
char str[] = "blabla";
strcpy(str, "abcabc");
är ok medan
strcpy("blabla", "abcabc");
är "undefined behaviour" (t.ex segmentation fault)
Du borde dock ha fått en varning vid funktionsanropet?
Sv:Skriva på sträng
Jag har även provat att skapa en ny sträng i funktionen och kopiera över strängen dit, men jag får ändå segmentation error när jag skriver över med "\0\0".Sv: Skriva på sträng
Orsaken till felet är att du inte initsierat vaiabeln "i" till något.
Programmet får svårt att utvärdera den, när det ska in i while loopen.
//HåkanSv:Skriva på sträng
Måhända är jag helt snurrig nu, men vad jag vet så skall alla moderna kompilatorer ha sina c-strängfunktioner i <cstring> och alla sina vanliga i <string>. Den gamla <string.h> är jag osäker på vilken det motsvarar.
<b>>Jag VILL lära mig c-strängar! :P</b>
Kan jag gå med på, men ett starkt råd är verkligen att undvika dem. Det finns i princip bara ett enda tillfälle när man måste eller ens bör använda dem, och det är när man har någon äldre funktion som returnerar eller kräver en c-sträng.
<b>>Orsaken till felet är att du inte initsierat vaiabeln "i" till något.</b>
Där var det!
Kombination av att varken i eller pointer_2 var initierad är felet.Sv: Skriva på sträng
De "gamla" headerfilerna för C har i C++ fått ett c tillagt i början av namnet samtidigt som man skippar ändelsen. Så string.h har blivit cstring.
Men om man inte behöver något som definieras i en headerfil, behöver man inte nödvändigtvis inkludera filen. Vanliga deklarationer kan man oftast skippa. Själva funktionerna ligger i binärfiler och länkas in ändå. Därför fungerar koden utan att #include <cstring> används.Sv:Skriva på sträng
int i=0;
och
pointer_2=NULL;
innan loopen men jag får fortfarande segmentation error av den där strncpy();. Jag provade att lägga koden utanför loopen men det fungerade ändå inte, strncpy(); ger ändå fel :(.
Anropet till print() ser ut så här.
char *strings[4]={ "ETT", "TVA", "TRE" };
out.print("hej dd what's dd up? dd ",strings,3);
print() är en metod som ni ser, men det borde väl inte spela någon roll i detta fallet...
Tack för ert tålamod :P.
Sv: Skriva på sträng
<b>Om det är en konstant sträng t.ex. "abcdd" så kan du få ett segmentation fault om compilatorn placerat strängen i read-only-memory.</b>Sv: Skriva på sträng
Felet uppstår när du i anropet till strncpy anger att du ska kopiera 2 tecken.
"\0\0" är nämligen lika med 0.
För att det ska fungera med koden du har nu får du skriva:
strncpy( pointer_2 , "\0\0", 0 );
Å andra sidan kan du remma raden helt, den gör inget vettigt ändå.
//Håkan
Sv:Skriva på sträng
Varför skulle det där leda till "segmentation fault"?Sv: Skriva på sträng
Det beror på strncpy, den försöker kopiera det antal bytes som angivits, som inte "finns".
M.a.o läsa minne som inte finns allokerat.
Första raden i assemblerkoden läser från "\0\0" och lägger i A1.
Här smäller det i assemblerkoden (sista raden).
<code>
mov al,byte ptr [esi] ; load byte from source
add esi,1
mov [edi],al ; store byte to dest
</code>
//HåkanSv:Skriva på sträng
Sv:Skriva på sträng
Vad då 'inte "finns"'? Nolltecknena finns ju.Sv: Skriva på sträng
Justering: Det är char pekaren som är problemet (har inte har en riktig null terminering? antar jag).
strncpy vill ha en char[] som argument, då fungerar det (null terminering).
Här blir det noga med längden på char[], och längden på data som ska tilldelas/kopieras.
Samt med nulltermineringen.
Vet inte riktigt vad ett read-only segment (processorn?) skulle vara i det här fallet,
och i så fall varför den (kompilatorn) skulle placera den där.
Förklara gärna, kul med ny kunskap.
//HåkanSv: Skriva på sträng
Ja finns, men "räknas" inte.."
Många (alla??) (cstring) strängfunktioner bortser från nulltecknet.
Rätta mig, jag har haft fel förut.. :-)
//HåkanSv:Skriva på sträng
Alltså, om du har en konstant sträng, typ const char[]="arne", så är den readonly (i princip). I vissa fall går det att ändra den ändå, men den ska inte kunna ändras.
Om du använder en literal, typ 3, NULL, eller "arne", så ligger de i en speciell minnesarea som programmet inte kan ändra. Hade alltså argumentet varit en "riktig" sträng, med motsvarande sträng tilldelad, hade det fungerat. typ char * sträng= "lång mening"
Fast som sagt. Allt det här hade lätt kunnat undvikas om man hade använt de mångfaldigt bättre funktionerna och klasserna från C++.Sv: Skriva på sträng
Är helt med på const och vad det innebär.
Om det är const som avses, så är det uttrycket read-only segment som dribblade bort mig.
Har aldrig hört/läst det så, men som jag skrev ovan, kul med nya kunskaper.
//HåkanSv:Skriva på sträng
Den enda const deklarationen jag vet som är aktuell i det här fallet, är andra argumentet till strncpy.
Vilket då blir "\0\0".
Detta för att strncpy inte ska ändra på argumentet (som Niklas skrev ovan).
(sparkar in öppna dörrar ;-) ).
Om nu const (read-only) deklarationen är ett problem borde den ju aldrig fungera, eller....??
Däremot om man tillhanda håller en char[] (med nullterminering) som första argument så fungerar det.
D.v.s. med "\0\0" och 2 som andra och tredje argument.
Det borde inte fungera om const är problemet, eller...??
//HåkanSv: Skriva på sträng
Ok, då får vi aldrig ändra en literal eller en konstant, för då får vi ett fel. Det vill säga att vi kan inte göra så här:
const char * str="Martin";
str="Göran"; //fel!
"Lasse"="Göran"; //lika fel!
strncpy(str, "Göran", 5); //lika fel!
strncpy("Lasse", "Göran", 5); //lika fel!
Det som i praktiken sker i programmet är sista varianten, man försöker använda strncpy på en literal. Vad som står i andra argumentet spelar ingen roll, och att "\0\0" är konstant ger alltså ingen skillnad.
Däremot är anropet till print med en literal felaktig.
Edit:
Anropet bör alltså se ut något sånt här;
char *strings[4]={ "ETT", "TVA", "TRE" };
char *string="hej dd what's dd up? dd ";
out.print(string, strings, 3);
Men som sagt så hade det här varit busenkelt om man hade använt C++ på rätt sätt. Det är synd att folk inte ger sig på att använda det ordentligt.
Sv:Skriva på sträng
char *strings[4]={ "ETT", "TVA", "TRE" };
char string[]="hej dd what's dd up? dd ";
out.print(string, strings, 3);
fungerar allting perfekt :).
Jag har också lärt mig rätt så mycket av den här tråden :D.
Anledningen till att jag vill lära mig att hålla på med c-strängar är att jag vill veta vad som pågår "under huven" och kunna hålla på med lågnivåprogrammering där det inte finns bibliotek som <string>, det är något av en "dröm" :P.
Tack så mycket.
/Pablo
Sv:Skriva på sträng
>och i så fall varför den (kompilatorn) skulle placera den där.
>Förklara gärna, kul med ny kunskap.
Varje del av minnet (segment) som en process använder har vissa egenskaper (I det här fallet menar jag logiska egenskaper inte fysiska). Windows sätter attribut som beskriver egenskaperna och sen ser hårdvaran i processorn till att de efterlevs.
Attributen är:
1. Read - processen får läsa i det här minnet
2. Write - processen får skriva i det här minnet
3. Execute - processen för köra program i det här minnet
Sen finns det några andra varianter som har med det virtuella minnet att göra (tex om ett visst minnessegment just nu finns i datorns internminne eller på hårddisken)
Hur fördelas då minnet när man laddar ett program:
1. Själva programkoden får såklart attributet "Execute" så att programmet kan köras. Vanligtvis får det också attributet "Read" eftersom kompilatorer ofta lägger konstanter tillsammans med koden (strängen som diskuteras i den här tråden är ett bra exempel). Dock får processen inte skriva över sin egen programkod och därför får segmentet inte attributet "write" (vilket ger segmentation fault om man försöker). Förr (80-talet) var det populärt att använda självmodiferande kod eftersom det var snabbare men nuförtiden är det nog bara virus som använder sig av det för att försvåra upptäckt.
2. Variabler och stack för programmet får naturligtvis attributen "read" & "write" men inte "execute". Detta innebär att du inte kan stoppa in assemblerkod i en variabel och sedan köra den koden. Detta används främst av virus genom s.k. buffer overrun. Problemet här har varit att hårdvaran i de flesta CPU:er inte kollar "execute" attributet så buffer-overrun fungerar iallafall. AMD64 (och troligen andra nyare CPU:er) har denna funktion. Ni har väl hört reklamen om AMD64:s inbyggda virusskydd. Nu vet ni vad det är också.Sv: Skriva på sträng
De har fått kritik för falsk marknadsföring eftersom processorn egentligen inte innehåller ett generellt "virusskydd" utan bara skyddar mot vissa typer av virus.
Kort sammanfattning av orsaken till kraschen:
Den sträng som Pablo försökte skriva i låg i skrivskyddat minne.Sv: Skriva på sträng
Kul att det här forumet lever upp lite grand.
Tack för din förklaring.
void print( char string[], char* strings[], int argc )
{
int i = 0;
char strarr[40];
char* pointer_2 = NULL;
strncpy( strarr , string, strlen( string ) );
while( i < argc )
{
pointer_2 = strstr( strarr, "dd" ); //Hittar "dd" i strängen och sätter en pekare att peka dit
if( pointer_2 == NULL )
{
printf( "null!!\n" );
break;
}
strncpy( pointer_2 , "\0\0", 2 ); // <------ Denna rad generar ett segmentation error.
printf( "%s\n\n", strarr ); //Skriver ut
printf( "%s\n", strings[i] );
strncpy( strarr , &*( pointer_2 + 2 ), 40 );
i++;
}
}
int main()
{
char* strings[4] = { "ETT", "TVA", "TRE" };
print( "hej dd what's dd up? dd ", strings, 3 );
return 0;
}
Bifogar ovanstående (fungerande) kod för provkörning/granskning.
Det är samma kod som Pablo hade, MEN jag har bytt ut pointer_1 mot en char[40].
Detta för att få kontroll (som jag skrev ovan) på strängen (nulltermineringen eg. längden data).
Nu fungerar anropet:
print( "hej dd what's dd up? dd ", strings, 3 );
Pekar bara på att det inte har med anropet (enbart) av print att göra.
Det spelar roll vad som händer sedan också.
OBS detta bara för att vidare belysa/diskutera.
Kul med lite liv i C++ forumet... :-)
Pablo lär sig saker och jag med.
//Håkan
Sv:Skriva på sträng
const char * str="Martin";
str="Göran"; //fel!
Detta är inget fel. "str" är en variabel som innehåller en pekare till en konstant men variabeln isig är inte konstant
utan kan ändras.
const char* str="Martin";
*str = 'x'; // fel: str innehåller en pekare till en konstant
str = "Göran"; // ok: str innehåller nu en pekare till en annan konstant
char* str1 = "Lasse";
str1 = str; // fel: str1 måste innehålla en pekare till icke-konstant.
const char* str2 = str1; // ok: pekare till konstanter kan också peka på icke-konstanter.
*str2 = 'x'; //fel: str2 ineehåller en pekare till en konstant (spelar ingen roll att det inte är en verklig konstant)
*const_cast<char*>(str2) = 'x'; // ok: ta bort const eftersom str2 inte pekar på en verklig konstant.
*const_cast<char*>(str1) = 'x'; // undefined behaviour (t.ex. segmentation fault) eftersom str1 pekar på en verklig konstant.
För att göra variabeln str konstant så att den inte kan ändras
const char* const str = "Martin";
str = "Göran"; // fel!
<code>
"Lasse"="Göran"; //lika fel!
</code>
lika fel som vaddå? Anledningen till att ovanstående inte fungerar har inget med const att göra. Det fungerar inte eftersom "Lasse" inte är en variabel och således inte kan tilldelas ett värde.
Sv: Skriva på sträng
char mar[] = "Martin";
char* const str = mar;
*str = 'P'; // ok: det str pekar på får ändras
str = "Göran"; // fel! str får inte ändras
Sv:Skriva på sträng
Eller rättare sagt tänkte inte alls. Det är naturligtvis helt rätt som Martin säger. Man får ha koll på var man sätter sina const!
Hade det nu varit frågan om string istället så hade jag ju bara skrivit const string, och inte behövt tänka en sekund mer på det...